Upload 8 files
Browse files- modules/dados.py +257 -0
- modules/evo.py +436 -0
- modules/ml.py +154 -0
- modules/otimiza.py +120 -0
- modules/planilha.py +247 -0
- modules/rl.py +1477 -0
- modules/shared_state.py +3 -0
- modules/utils.py +47 -0
modules/dados.py
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import plotly.express as px
|
| 3 |
+
import plotly.graph_objects as go
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from math import radians, sin, cos, sqrt, atan2
|
| 6 |
+
import googlemaps
|
| 7 |
+
import locale
|
| 8 |
+
from .shared_state import state # Importa o estado compartilhado
|
| 9 |
+
|
| 10 |
+
# Inicializando o cliente Google Maps com a chave da API
|
| 11 |
+
gmaps = googlemaps.Client(key='AIzaSyDoJ6C7NE2CHqFcaHTnhreOfgJeTk4uSH0')
|
| 12 |
+
|
| 13 |
+
# Configurando o locale para português do Brasil
|
| 14 |
+
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
|
| 15 |
+
|
| 16 |
+
# Load the data
|
| 17 |
+
data_path = 'data_2.xlsx'
|
| 18 |
+
df = pd.read_excel(data_path, sheet_name='Planilha1')
|
| 19 |
+
# Garantir que a coluna "Data" seja do tipo datetime
|
| 20 |
+
df['Data'] = pd.to_datetime(df['Data'], errors='coerce')
|
| 21 |
+
|
| 22 |
+
# Função para calcular a distância haversine
|
| 23 |
+
def haversine(lat1, lon1, lat2, lon2):
|
| 24 |
+
raio_terra = 6371000 # Raio da Terra em metros
|
| 25 |
+
dlat = radians(lat2 - lat1)
|
| 26 |
+
dlon = radians(lon2 - lon1)
|
| 27 |
+
a = sin(dlat / 2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon / 2)**2
|
| 28 |
+
c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
| 29 |
+
return raio_terra * c
|
| 30 |
+
|
| 31 |
+
# Função unificada para gerar mapa, estatísticas, DataFrame filtrado e arquivo de download
|
| 32 |
+
def unified_action(selected_types, selected_bairros, selected_fonte, min_atotal, max_atotal, min_apriv, max_apriv, min_ater, max_ater, min_date, max_date, address=None, radius=None):
|
| 33 |
+
filtered_df = df.copy()
|
| 34 |
+
|
| 35 |
+
# Filtrando os dados com base nos tipos
|
| 36 |
+
if "Todos" not in selected_types:
|
| 37 |
+
filtered_df = filtered_df[filtered_df['Tipo'].isin(selected_types)]
|
| 38 |
+
|
| 39 |
+
# Filtrando os dados com base nos bairros
|
| 40 |
+
if "Todos" not in selected_bairros:
|
| 41 |
+
filtered_df = filtered_df[filtered_df['Bairro'].isin(selected_bairros)]
|
| 42 |
+
|
| 43 |
+
# Filtrando os dados com base na fonte
|
| 44 |
+
if "Todos" not in selected_fonte:
|
| 45 |
+
filtered_df = filtered_df[filtered_df['Fonte'].isin(selected_fonte)]
|
| 46 |
+
|
| 47 |
+
# Filtrando por área total
|
| 48 |
+
if min_atotal is not None:
|
| 49 |
+
filtered_df = filtered_df[filtered_df['Atotal'] >= min_atotal]
|
| 50 |
+
if max_atotal is not None:
|
| 51 |
+
filtered_df = filtered_df[filtered_df['Atotal'] <= max_atotal]
|
| 52 |
+
|
| 53 |
+
# Filtrando por área privativa
|
| 54 |
+
if min_apriv is not None:
|
| 55 |
+
filtered_df = filtered_df[filtered_df['Apriv'] >= min_apriv]
|
| 56 |
+
if max_apriv is not None:
|
| 57 |
+
filtered_df = filtered_df[filtered_df['Apriv'] <= max_apriv]
|
| 58 |
+
|
| 59 |
+
# Filtrando por área de terreno
|
| 60 |
+
if min_ater is not None:
|
| 61 |
+
filtered_df = filtered_df[filtered_df['Ater'] >= min_ater]
|
| 62 |
+
if max_ater is not None:
|
| 63 |
+
filtered_df = filtered_df[filtered_df['Ater'] <= max_ater]
|
| 64 |
+
|
| 65 |
+
# Filtrando por data, se especificada
|
| 66 |
+
if min_date is not None:
|
| 67 |
+
filtered_df = filtered_df[filtered_df['Data'] >= pd.to_datetime(min_date)]
|
| 68 |
+
|
| 69 |
+
if max_date is not None:
|
| 70 |
+
filtered_df = filtered_df[filtered_df['Data'] <= pd.to_datetime(max_date)]
|
| 71 |
+
|
| 72 |
+
# Filtrando por endereço e raio, se fornecidos
|
| 73 |
+
if address:
|
| 74 |
+
try:
|
| 75 |
+
result = gmaps.geocode(address)
|
| 76 |
+
if result:
|
| 77 |
+
coordinates = result[0]['geometry']['location']
|
| 78 |
+
center_lat = coordinates['lat']
|
| 79 |
+
center_lon = coordinates['lng']
|
| 80 |
+
|
| 81 |
+
if radius: # Se o raio for especificado
|
| 82 |
+
# Calculando distâncias e filtrando
|
| 83 |
+
filtered_df['Distance'] = filtered_df.apply(
|
| 84 |
+
lambda row: haversine(center_lat, center_lon, row['Latitude'], row['Longitude']),
|
| 85 |
+
axis=1
|
| 86 |
+
)
|
| 87 |
+
filtered_df = filtered_df[filtered_df['Distance'] <= radius]
|
| 88 |
+
else: # Se nenhum raio for especificado
|
| 89 |
+
# Filtrar pelo nome da rua ou bairro
|
| 90 |
+
address_components = result[0]['address_components']
|
| 91 |
+
street = None
|
| 92 |
+
neighborhood = None
|
| 93 |
+
|
| 94 |
+
for component in address_components:
|
| 95 |
+
if 'route' in component['types']: # Rua
|
| 96 |
+
street = component['long_name']
|
| 97 |
+
if 'sublocality' in component['types'] or 'locality' in component['types']: # Bairro
|
| 98 |
+
neighborhood = component['long_name']
|
| 99 |
+
|
| 100 |
+
if street:
|
| 101 |
+
filtered_df = filtered_df[filtered_df['Localização'].str.contains(street, na=False)]
|
| 102 |
+
elif neighborhood:
|
| 103 |
+
filtered_df = filtered_df[filtered_df['Bairro'].str.contains(neighborhood, na=False)]
|
| 104 |
+
except Exception as e:
|
| 105 |
+
print(f"Erro ao filtrar pelo endereço: {e}")
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
# Criando o mapa
|
| 109 |
+
fig = px.scatter_mapbox(
|
| 110 |
+
filtered_df,
|
| 111 |
+
lat="Latitude",
|
| 112 |
+
lon="Longitude",
|
| 113 |
+
hover_name="Localização",
|
| 114 |
+
hover_data={
|
| 115 |
+
"Valor": True,
|
| 116 |
+
"Vunit_priv": True,
|
| 117 |
+
"Tipo": True,
|
| 118 |
+
"url": True,
|
| 119 |
+
},
|
| 120 |
+
color="Tipo",
|
| 121 |
+
size="Valor",
|
| 122 |
+
size_max=30,
|
| 123 |
+
zoom=10
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
fig.update_layout(
|
| 127 |
+
mapbox_style="open-street-map",
|
| 128 |
+
autosize=True,
|
| 129 |
+
height=600,
|
| 130 |
+
margin={"r": 0, "t": 0, "l": 0, "b": 0},
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
# Adicionando círculo ao mapa se endereço e raio forem fornecidos
|
| 134 |
+
if address and radius:
|
| 135 |
+
try:
|
| 136 |
+
raio_terra = 6371000
|
| 137 |
+
pontos_lat = []
|
| 138 |
+
pontos_lon = []
|
| 139 |
+
for angulo in range(360):
|
| 140 |
+
ang_rad = radians(angulo)
|
| 141 |
+
d_lat = (radius / raio_terra) * cos(ang_rad)
|
| 142 |
+
d_lon = (radius / (raio_terra * cos(radians(center_lat)))) * sin(ang_rad)
|
| 143 |
+
pontos_lat.append(center_lat + d_lat * (180 / 3.141592653589793))
|
| 144 |
+
pontos_lon.append(center_lon + d_lon * (180 / 3.141592653589793))
|
| 145 |
+
|
| 146 |
+
fig.add_trace(go.Scattermapbox(
|
| 147 |
+
lat=pontos_lat,
|
| 148 |
+
lon=pontos_lon,
|
| 149 |
+
mode='lines',
|
| 150 |
+
fill='toself',
|
| 151 |
+
fillcolor='rgba(0, 0, 255, 0.2)',
|
| 152 |
+
line=dict(color='blue', width=2),
|
| 153 |
+
name='Raio'
|
| 154 |
+
))
|
| 155 |
+
|
| 156 |
+
fig.add_trace(go.Scattermapbox(
|
| 157 |
+
lat=[center_lat],
|
| 158 |
+
lon=[center_lon],
|
| 159 |
+
mode='markers',
|
| 160 |
+
marker=dict(size=10, color='red'),
|
| 161 |
+
name='Centro'
|
| 162 |
+
))
|
| 163 |
+
except Exception as e:
|
| 164 |
+
print(f"Erro ao adicionar o círculo no mapa: {e}")
|
| 165 |
+
|
| 166 |
+
# Gerando estatísticas
|
| 167 |
+
if filtered_df.empty:
|
| 168 |
+
stats_text = "Nenhum dado disponível."
|
| 169 |
+
else:
|
| 170 |
+
stats_text = (f"Quantidade de Dados: {len(filtered_df):n}\n"
|
| 171 |
+
f"Média do Valor: {locale.format_string('%.2f', filtered_df['Valor'].mean(), grouping=True)}\n"
|
| 172 |
+
f"Máximo do Valor: {locale.format_string('%.2f', filtered_df['Valor'].max(), grouping=True)}\n"
|
| 173 |
+
f"Mínimo do Valor: {locale.format_string('%.2f', filtered_df['Valor'].min(), grouping=True)}")
|
| 174 |
+
|
| 175 |
+
# Salvando DataFrame filtrado em arquivo CSV
|
| 176 |
+
file_path = "dados_filtrados.xlsx"
|
| 177 |
+
filtered_df.to_excel(file_path)
|
| 178 |
+
|
| 179 |
+
return fig, stats_text, filtered_df, file_path
|
| 180 |
+
|
| 181 |
+
# List of unique property types, bairros, and fonte, with "Todos" as default
|
| 182 |
+
property_types = ["Todos"] + sorted(df['Tipo'].dropna().unique().tolist())
|
| 183 |
+
bairro_list = ["Todos"] + sorted(df['Bairro'].dropna().unique().tolist())
|
| 184 |
+
fonte_list = ["Todos"] + sorted(df['Fonte'].dropna().unique().tolist())
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def dados_tab():
|
| 188 |
+
with gr.Tab("Pesquisar Dados"):
|
| 189 |
+
toggle_filters = gr.Checkbox(label="Exibir Campos de Pesquisa", value=True)
|
| 190 |
+
|
| 191 |
+
with gr.Row(visible=True) as filters:
|
| 192 |
+
tipo_filter = gr.Dropdown(label="Selecione os Tipos de Imóvel", choices=property_types, value=["Todos"], multiselect=True)
|
| 193 |
+
bairro_filter = gr.Dropdown(label="Selecione os Bairros", choices=bairro_list, value=["Todos"], multiselect=True)
|
| 194 |
+
fonte_filter = gr.Dropdown(label="Selecione a Fonte", choices=fonte_list, value=["Todos"], multiselect=True)
|
| 195 |
+
|
| 196 |
+
with gr.Row(visible=True) as area_filters:
|
| 197 |
+
min_atotal = gr.Number(label="Área Total Mínima", value=None)
|
| 198 |
+
max_atotal = gr.Number(label="Área Total Máxima", value=None)
|
| 199 |
+
min_apriv = gr.Number(label="Área Privativa Mínima", value=None)
|
| 200 |
+
max_apriv = gr.Number(label="Área Privativa Máxima", value=None)
|
| 201 |
+
min_ater = gr.Number(label="Área Terreno Mínima", value=None)
|
| 202 |
+
max_ater = gr.Number(label="Área Terreno Máxima", value=None)
|
| 203 |
+
|
| 204 |
+
with gr.Row(visible=True) as date_filters:
|
| 205 |
+
min_date = gr.Textbox(label="Data Inicial (AAAA-MM-DD)", value="")
|
| 206 |
+
max_date = gr.Textbox(label="Data Final (AAAA-MM-DD)", value="")
|
| 207 |
+
|
| 208 |
+
with gr.Row(visible=True) as address_filters:
|
| 209 |
+
address_input = gr.Textbox(label="Endereço (Opcional)")
|
| 210 |
+
radius_input = gr.Number(label="Raio em metros (Opcional)")
|
| 211 |
+
|
| 212 |
+
with gr.Row():
|
| 213 |
+
search_button = gr.Button("Pesquisar")
|
| 214 |
+
clear_button = gr.Button("Limpar")
|
| 215 |
+
|
| 216 |
+
map_output = gr.Plot()
|
| 217 |
+
stats_output = gr.Textbox(lines=4, label="Estatísticas")
|
| 218 |
+
filtered_df_output = gr.DataFrame(label="Dados Filtrados")
|
| 219 |
+
download_output = gr.File(label="Baixar Dados Filtrados")
|
| 220 |
+
|
| 221 |
+
def clear_action():
|
| 222 |
+
return (["Todos"], ["Todos"], ["Todos"], None, None, None, None, None, None, "", "", "", None, None, "", pd.DataFrame(), None)
|
| 223 |
+
|
| 224 |
+
toggle_filters.change(
|
| 225 |
+
lambda show: (
|
| 226 |
+
gr.update(visible=show),
|
| 227 |
+
gr.update(visible=show),
|
| 228 |
+
gr.update(visible=show),
|
| 229 |
+
gr.update(visible=show) # Adiciona o filtro de data na visibilidade
|
| 230 |
+
),
|
| 231 |
+
inputs=[toggle_filters],
|
| 232 |
+
outputs=[filters, area_filters, address_filters, date_filters]
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
search_button.click(
|
| 237 |
+
unified_action,
|
| 238 |
+
inputs=[tipo_filter, bairro_filter, fonte_filter, min_atotal, max_atotal, min_apriv, max_apriv,
|
| 239 |
+
min_ater, max_ater, min_date, max_date, address_input, radius_input],
|
| 240 |
+
outputs=[map_output, stats_output, filtered_df_output, download_output]
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
clear_button.click(
|
| 244 |
+
clear_action,
|
| 245 |
+
outputs=[tipo_filter, bairro_filter, fonte_filter, min_atotal, max_atotal, min_apriv, max_apriv,
|
| 246 |
+
min_ater, max_ater, min_date, max_date, address_input, radius_input,
|
| 247 |
+
map_output, stats_output, filtered_df_output, download_output]
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
return locals(), filtered_df_output
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
### implementar o KNN
|
| 254 |
+
### Arrumar a questão de que somente clicando no botão "Limpar" é possível acionar os filtros
|
| 255 |
+
### Filtro por data
|
| 256 |
+
### Zoon oelo filtro de raio
|
| 257 |
+
### Gráfico de linhas
|
modules/evo.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import pandas as pd
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
from docx import Document
|
| 5 |
+
from docx.shared import Pt
|
| 6 |
+
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
| 7 |
+
from num2words import num2words
|
| 8 |
+
import warnings # Somente se voc� realmente quer suprimir avisos
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# Suprimir todos os avisos durante a execução do script
|
| 12 |
+
warnings.filterwarnings("ignore")
|
| 13 |
+
|
| 14 |
+
# Calcular a data mais recente do CUB fora da função
|
| 15 |
+
df_cub = pd.read_excel('TABELAS_evo.xlsx', sheet_name='CUB')
|
| 16 |
+
ultimo_cub = df_cub.columns[-1]
|
| 17 |
+
|
| 18 |
+
#função para escrever por extenso o valor atribuído
|
| 19 |
+
def numero_por_extenso(numero):
|
| 20 |
+
reais = int(numero)
|
| 21 |
+
|
| 22 |
+
extenso_reais = num2words(reais, lang='pt_BR')
|
| 23 |
+
|
| 24 |
+
if reais == 1:
|
| 25 |
+
extenso_reais += ' real'
|
| 26 |
+
else:
|
| 27 |
+
extenso_reais += ' reais'
|
| 28 |
+
|
| 29 |
+
return extenso_reais
|
| 30 |
+
|
| 31 |
+
# Função para calcular o valor do imóvel
|
| 32 |
+
def calcular_valor_imovel(
|
| 33 |
+
area=100, data_refer_str=None, data_const_str=None, tipo_cub="R 1-N (Res. Unifamiliar)",
|
| 34 |
+
data_cub_str=None, percentual_cub=1, est_custo_dir="-", BDI=22.5, BDI_tipo ="Arbitrado", fator_local = 1,
|
| 35 |
+
just_fator_local = "-", tipologia="APARTAMENTOS",
|
| 36 |
+
estado="B - entre novo e regular", VR=0.0, deprec="Arbitrada",
|
| 37 |
+
valor_terreno=0,
|
| 38 |
+
est_ter="Grau III",
|
| 39 |
+
fc=1, fc_just="Arbitrado"):
|
| 40 |
+
|
| 41 |
+
# Carregar dados dos arquivos Excel
|
| 42 |
+
df_cub = pd.read_excel('TABELAS_evo.xlsx', sheet_name='CUB')
|
| 43 |
+
df_vida = pd.read_excel('TABELAS_evo.xlsx', sheet_name='VUTIL')
|
| 44 |
+
df_dep = pd.read_excel('TABELAS_evo.xlsx', sheet_name='DEP')
|
| 45 |
+
df_estado_cons = pd.read_excel('TABELAS_evo.xlsx', sheet_name='CONS')
|
| 46 |
+
|
| 47 |
+
# Converter datas de entrada
|
| 48 |
+
if data_refer_str is None or data_refer_str == "":
|
| 49 |
+
data_refer = datetime.now()
|
| 50 |
+
else:
|
| 51 |
+
data_refer = datetime.strptime(data_refer_str, "%m/%Y")
|
| 52 |
+
data_const = datetime.strptime(data_const_str, "%m/%Y")
|
| 53 |
+
if data_cub_str is None or data_cub_str == "":
|
| 54 |
+
data_cub = datetime.now()
|
| 55 |
+
else:
|
| 56 |
+
data_cub = datetime.strptime(data_cub_str, "%m/%Y")
|
| 57 |
+
|
| 58 |
+
# Filtrar por tipo_cub
|
| 59 |
+
df_tipo_cub = df_cub[df_cub['CÓDIGO'] == tipo_cub]
|
| 60 |
+
|
| 61 |
+
# Obter o valor do CUB na coluna correspondente à data
|
| 62 |
+
valor_cub_column = data_cub.strftime("%m/%Y")
|
| 63 |
+
valor_cub = df_tipo_cub.at[df_tipo_cub.index[0], valor_cub_column]
|
| 64 |
+
|
| 65 |
+
# Idade
|
| 66 |
+
idade = data_refer.toordinal() - data_const.toordinal()
|
| 67 |
+
if idade > 1:
|
| 68 |
+
idade = idade // 365
|
| 69 |
+
else:
|
| 70 |
+
idade = 1
|
| 71 |
+
|
| 72 |
+
# Filtrar por %de Vida
|
| 73 |
+
vdu = df_vida.loc[(df_vida['FINAL'] == tipologia)]
|
| 74 |
+
|
| 75 |
+
# % de vida útil
|
| 76 |
+
perc_vdu = (idade / vdu['VIDAUTIL']) * 100
|
| 77 |
+
perc_vdu = int(round(perc_vdu, 0))
|
| 78 |
+
|
| 79 |
+
if perc_vdu >= 100:
|
| 80 |
+
percentual_vdu = 100
|
| 81 |
+
elif perc_vdu < 2:
|
| 82 |
+
percentual_vdu = 2
|
| 83 |
+
else:
|
| 84 |
+
percentual_vdu = perc_vdu
|
| 85 |
+
|
| 86 |
+
# Filtrar conforme o % de vida útil
|
| 87 |
+
df_conserv = df_dep.loc[df_dep['%deVida'] == percentual_vdu]
|
| 88 |
+
|
| 89 |
+
# Converter o valor residual de string para número (float)
|
| 90 |
+
try:
|
| 91 |
+
VR = float(VR.strip()) if isinstance(VR, str) and VR.strip() != "" else 0.0
|
| 92 |
+
except ValueError:
|
| 93 |
+
VR = 0.0
|
| 94 |
+
# Obter da depreciação
|
| 95 |
+
coef_HH = float(df_conserv[estado]/100)
|
| 96 |
+
coef_HH = round(coef_HH, 3)
|
| 97 |
+
|
| 98 |
+
# Valor do "Kd"
|
| 99 |
+
kd = VR + (1 - coef_HH) * (1 - VR)
|
| 100 |
+
kd = round(kd, 3)
|
| 101 |
+
|
| 102 |
+
# Converter o valor do terreno de string para número (float)
|
| 103 |
+
#try:
|
| 104 |
+
#valor_terreno = float(valor_terreno.strip()) if isinstance(valor_terreno, str) and valor_terreno.strip() != "" else 0.0
|
| 105 |
+
#except ValueError:
|
| 106 |
+
#valor_terreno = 0.0
|
| 107 |
+
|
| 108 |
+
# Cálculos
|
| 109 |
+
Valor_sem_deprec = round(area * valor_cub * percentual_cub * (1 + BDI / 100) * fator_local, 2)
|
| 110 |
+
Valor_com_deprec = Valor_sem_deprec * kd
|
| 111 |
+
Valor_com_deprec = round(Valor_com_deprec, 2)
|
| 112 |
+
valor_imovel = round((valor_terreno + Valor_com_deprec) * float(fc), 0)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
# Atributo da coluna "cons" pelo qual você deseja fazer a correspondência
|
| 116 |
+
atributo_desejado = estado
|
| 117 |
+
# Encontrar o valor da coluna "obs" com base no atributo da coluna "cons"
|
| 118 |
+
valor_obs = df_estado_cons.loc[df_estado_cons['cons'] == atributo_desejado, 'obs'].iloc[0]
|
| 119 |
+
|
| 120 |
+
# -------------------------------------- #
|
| 121 |
+
|
| 122 |
+
# GRAU DE FUNDAMENTAÇÃO CUSTOS
|
| 123 |
+
|
| 124 |
+
# item_1 - Graus de Fundamentação (Estimativa do custo direto)
|
| 125 |
+
if est_custo_dir == "Elaboração de orçamento, no mínimo sintético":
|
| 126 |
+
cust_1 = 3
|
| 127 |
+
elif est_custo_dir == "Utilização CUB para projeto semelhante ao projeto padrão":
|
| 128 |
+
cust_1 = 2
|
| 129 |
+
else:
|
| 130 |
+
cust_1 = 1
|
| 131 |
+
|
| 132 |
+
# item_2 - Graus de Fundamentação (BDI)
|
| 133 |
+
if BDI_tipo == "Calculado":
|
| 134 |
+
cust_2 = 3
|
| 135 |
+
elif BDI_tipo == "Justificado":
|
| 136 |
+
cust_2 = 2
|
| 137 |
+
else:
|
| 138 |
+
cust_2 = 1
|
| 139 |
+
|
| 140 |
+
# item_3 - Graus de Fundamentação (Depreciação física)
|
| 141 |
+
if deprec == "Por levantamento do custo de recuperação do bem, para deixá-lo no estado de novo ou caso de bens novos ou projetos hipotéticos":
|
| 142 |
+
cust_3 = 3
|
| 143 |
+
elif deprec == "Por métodos técnicos consagrados, considerando-se idade, vida útil e estado de conservação":
|
| 144 |
+
cust_3 = 2
|
| 145 |
+
else:
|
| 146 |
+
cust_3 = 1
|
| 147 |
+
|
| 148 |
+
# enquadramento
|
| 149 |
+
soma_custo = cust_1 + cust_2 + cust_3
|
| 150 |
+
if soma_custo >= 7 and cust_1 == 3 and cust_2 >= 2 and cust_3 >= 2:
|
| 151 |
+
est_cr = "Grau III"
|
| 152 |
+
elif soma_custo >= 5 and cust_1 >= 2 and cust_2 >= 2:
|
| 153 |
+
est_cr = "Grau II"
|
| 154 |
+
elif soma_custo >= 3 and cust_1 >= 1 and cust_2 >= 1 and cust_3 >= 1:
|
| 155 |
+
est_cr = "Grau I"
|
| 156 |
+
else:
|
| 157 |
+
est_cr = "Fora dos critérios"
|
| 158 |
+
|
| 159 |
+
# -------------------------------------- #
|
| 160 |
+
|
| 161 |
+
# GRAU DE FUNDAMENTAÇÃO EVOLUTIVO
|
| 162 |
+
|
| 163 |
+
# item_1 - Graus de Fundamentação (Estimativa do valor do terreno)
|
| 164 |
+
if est_ter == "Grau III":
|
| 165 |
+
evo_1 = 3
|
| 166 |
+
elif est_ter == "Grau II":
|
| 167 |
+
evo_1 = 2
|
| 168 |
+
else:
|
| 169 |
+
evo_1 = 1
|
| 170 |
+
|
| 171 |
+
# item_2 - Graus de Fundamentação (Estimativa dos custos de reedição)
|
| 172 |
+
if est_cr == "Grau III":
|
| 173 |
+
evo_2 = 3
|
| 174 |
+
elif est_cr == "Grau II":
|
| 175 |
+
evo_2 = 2
|
| 176 |
+
else:
|
| 177 |
+
evo_2 = 1
|
| 178 |
+
|
| 179 |
+
# item_3 - Graus de Fundamentação (Forma de cálculo do FC)
|
| 180 |
+
if fc_just == "Inferido em mercado semelhante":
|
| 181 |
+
evo_3 = 3
|
| 182 |
+
elif fc_just == "Justificado:":
|
| 183 |
+
evo_3 = 2
|
| 184 |
+
else:
|
| 185 |
+
evo_3 = 1
|
| 186 |
+
|
| 187 |
+
# enquadramento
|
| 188 |
+
soma_evo = evo_1 + evo_2 + evo_3
|
| 189 |
+
if soma_evo >= 8 and evo_1 == 3 and evo_2 == 3 and evo_3 >= 2: # confirmar!!!!!
|
| 190 |
+
fundamentacao_evolutivo = "Grau III"
|
| 191 |
+
elif soma_evo >= 5 and evo_1 >= 2 and evo_2 >= 2:
|
| 192 |
+
fundamentacao_evolutivo = "Grau II"
|
| 193 |
+
elif soma_evo >= 3 and evo_1 >= 1 and evo_2 >= 1 and evo_3 >= 1:
|
| 194 |
+
fundamentacao_evolutivo = "Grau I"
|
| 195 |
+
else:
|
| 196 |
+
fundamentacao_evolutivo = "Fora dos critérios"
|
| 197 |
+
|
| 198 |
+
# -------------------------------------- #
|
| 199 |
+
|
| 200 |
+
# criação de strings para os relatórios da interface e do word
|
| 201 |
+
|
| 202 |
+
#####
|
| 203 |
+
tipo_cub = tipo_cub.replace('.', '@').replace(',', '.').replace('@', ',')
|
| 204 |
+
est_custo_dir = est_custo_dir.replace('.', '@').replace(',', '.').replace('@', ',')
|
| 205 |
+
just_fator_local = just_fator_local.replace('.', '@').replace(',', '.').replace('@', ',')
|
| 206 |
+
valor_inicial = f"""
|
| 207 |
+
Área construída : {area:,.2f} m²
|
| 208 |
+
Data de referência: {data_refer.strftime("%m/%Y")}
|
| 209 |
+
Data da construção: {data_const_str}
|
| 210 |
+
Data do CUB: {data_cub.strftime("%m/%Y")}
|
| 211 |
+
Tipo de CUB: {tipo_cub}
|
| 212 |
+
Fator para adequação do CUB: {percentual_cub}
|
| 213 |
+
Estimativa do custo direto: {est_custo_dir}
|
| 214 |
+
BDI (%): {BDI}
|
| 215 |
+
Método de elaboração do BDI: {BDI_tipo}
|
| 216 |
+
Valor CUB: R$ {valor_cub:,.2f}
|
| 217 |
+
Fator local: {fator_local}
|
| 218 |
+
Fator local(justificativa): {just_fator_local}
|
| 219 |
+
Valor antes da depreciação = área construída * CUB * fator adequação CUB * (1 + BDI / 100) * fator local
|
| 220 |
+
Valor sem depreciação: R$ {Valor_sem_deprec:,.2f}
|
| 221 |
+
"""
|
| 222 |
+
|
| 223 |
+
# Substituindo ponto por vírgula
|
| 224 |
+
valor_inicial = valor_inicial.replace('.', '@')
|
| 225 |
+
valor_inicial = valor_inicial.replace(',', '.')
|
| 226 |
+
valor_inicial = valor_inicial.replace('@', ',')
|
| 227 |
+
|
| 228 |
+
#####
|
| 229 |
+
valor_obs = valor_obs.replace('.', '@').replace(',', '.').replace('@', ',')
|
| 230 |
+
deprec = deprec.replace('.', '@').replace(',', '.').replace('@', ',')
|
| 231 |
+
deprec = f"""
|
| 232 |
+
Tipologia: {tipologia}
|
| 233 |
+
Vida útil da tipologia: {int(vdu['VIDAUTIL'])}
|
| 234 |
+
Estado de conservação: {estado}
|
| 235 |
+
Estado de conservação - descrição: {valor_obs}
|
| 236 |
+
% de Vida Útil: {percentual_vdu}
|
| 237 |
+
Coeficiente de Depreciação: {coef_HH}
|
| 238 |
+
Valor residual (0, 0.1 ou 0.2): {VR}
|
| 239 |
+
Forma de cálculo da depreciação física: {deprec}
|
| 240 |
+
Kd: {kd}
|
| 241 |
+
onde: Kd = (Valor residual + (1 - percentual de depreciação)*(1 - Valor residual))
|
| 242 |
+
Valor depois da depreciação = (Valor antes da depreciação) x Kd (com coeficiente de valor residual)
|
| 243 |
+
Valor final construção: R$ {Valor_com_deprec:,.2f}
|
| 244 |
+
Especificação da Avaliação (benfeitorias) - Método da Quantificação do Custo: {est_cr}
|
| 245 |
+
"""
|
| 246 |
+
# Substituindo ponto por vírgula
|
| 247 |
+
deprec = deprec.replace('.', '@')
|
| 248 |
+
deprec = deprec.replace(',', '.')
|
| 249 |
+
deprec = deprec.replace('@', ',')
|
| 250 |
+
|
| 251 |
+
#####
|
| 252 |
+
valor_ter = f"""
|
| 253 |
+
Valor do Terreno: R$ {valor_terreno:,.2f}
|
| 254 |
+
Especificação da Avaliação (terreno ou gleba) - Método Comparativo ou Involutivo: {est_ter}
|
| 255 |
+
"""
|
| 256 |
+
# Substituindo ponto por vírgula
|
| 257 |
+
valor_ter = valor_ter.replace('.', '@')
|
| 258 |
+
valor_ter = valor_ter.replace(',', '.')
|
| 259 |
+
valor_ter = valor_ter.replace('@', ',')
|
| 260 |
+
|
| 261 |
+
#####
|
| 262 |
+
valor_ext = numero_por_extenso(valor_imovel)
|
| 263 |
+
|
| 264 |
+
v_relat = f"{valor_imovel:,.2f}"
|
| 265 |
+
v_relat = v_relat.replace('.', '@')
|
| 266 |
+
v_relat = v_relat.replace(',', '.')
|
| 267 |
+
v_relat = v_relat.replace('@', ',')
|
| 268 |
+
|
| 269 |
+
valor_final = f"""
|
| 270 |
+
FC (Fator de Comercialização): {fc}
|
| 271 |
+
Observação sobre o FC: {fc_just}
|
| 272 |
+
---------------------
|
| 273 |
+
VI = (VT + CA) * FC
|
| 274 |
+
Onde:
|
| 275 |
+
VI: Valor estimado do imóvel;
|
| 276 |
+
VT: Valor estimado do terreno;
|
| 277 |
+
CA: Custo de reedição das benfeitorias;
|
| 278 |
+
FC: Fator de comercialização.
|
| 279 |
+
---------------------
|
| 280 |
+
Valor do Imóvel (c/ arredondamento): R$ {v_relat}
|
| 281 |
+
({valor_ext})
|
| 282 |
+
---------------------
|
| 283 |
+
Especificação da Avaliação - Método Evolutivo: {fundamentacao_evolutivo}
|
| 284 |
+
"""
|
| 285 |
+
|
| 286 |
+
# -------------------------------------- #
|
| 287 |
+
|
| 288 |
+
# Criação de um relatório da avaliação no word
|
| 289 |
+
# Criar um novo documento do Word
|
| 290 |
+
doc = Document()
|
| 291 |
+
|
| 292 |
+
# Definir o título do documento
|
| 293 |
+
doc.add_heading('Relatório de Avaliação de Imóvel', level=1)
|
| 294 |
+
|
| 295 |
+
# Definir as seções do relatório
|
| 296 |
+
sections = [
|
| 297 |
+
(valor_inicial, "Valor Inicial da Construção"),
|
| 298 |
+
(deprec, "Cálculo da Depreciação"),
|
| 299 |
+
(valor_ter, "Valor Estimado para o Terreno"),
|
| 300 |
+
(valor_final, "Valor Final do Imóvel"),
|
| 301 |
+
]
|
| 302 |
+
|
| 303 |
+
for content, title in sections:
|
| 304 |
+
doc.add_heading(title, level=2)
|
| 305 |
+
p = doc.add_paragraph()
|
| 306 |
+
run = p.add_run(content)
|
| 307 |
+
run.font.size = Pt(12)
|
| 308 |
+
|
| 309 |
+
if title == "": # Se for a seção de assinatura
|
| 310 |
+
p.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # Define o alinhamento para à direita
|
| 311 |
+
else:
|
| 312 |
+
p.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT # Define o alinhamento para à esquerda
|
| 313 |
+
|
| 314 |
+
# Salvar o documento em um arquivo .doc
|
| 315 |
+
doc.save('relatorio_avaliacao.doc')
|
| 316 |
+
|
| 317 |
+
# -------------------------------------- #
|
| 318 |
+
|
| 319 |
+
# Outputs
|
| 320 |
+
return valor_inicial, deprec, valor_ter, valor_final, 'relatorio_avaliacao.doc'
|
| 321 |
+
|
| 322 |
+
# -------------------------------------- #
|
| 323 |
+
|
| 324 |
+
# Interface Gradio
|
| 325 |
+
|
| 326 |
+
def evo_tab():
|
| 327 |
+
with gr.Tab("Evolutivo"):
|
| 328 |
+
with gr.Row():
|
| 329 |
+
with gr.Column():
|
| 330 |
+
# Valor Inicial da Construção
|
| 331 |
+
with gr.Row():
|
| 332 |
+
area = gr.Number(label="Área (m²)", value=100, info="Área construída", scale=0.5) # Remova a vírgula aqui
|
| 333 |
+
data_r = gr.Textbox(label="Data referência (mm/aaaa)", value=ultimo_cub, info="Data do fato gerador da demanda", scale=1) # Remova a vírgula aqui
|
| 334 |
+
data_c = gr.Textbox(label="Data construção (mm/aaaa)", value=ultimo_cub, info="Data de construção do imóvel", scale=1)
|
| 335 |
+
with gr.Group():
|
| 336 |
+
with gr.Row():
|
| 337 |
+
cub = gr.Dropdown(label="Tipo de CUB",
|
| 338 |
+
choices=["R 1-B (Res. Unifamiliar)", "R 1-N (Res. Unifamiliar)", "R 1-A (Res. Unifamiliar)",
|
| 339 |
+
"PP 4-B (Prédio Popular)", "PP 4-N (Prédio Popular)", "R 8-B (Res. Multifamiliar)",
|
| 340 |
+
"R 8-N (Res. Multifamiliar)", "R 8-A (Res. Multifamiliar)", "R 16-N (Res. Multifamiliar)",
|
| 341 |
+
"R 16-A (Res. Multifamiliar)", "PIS (Projeto Inter. Social)", "RP1Q (Residência Popular)",
|
| 342 |
+
"CAL 8-N (Com. Andar Livres)", "CAL 8-A (Com. Andar Livres)",
|
| 343 |
+
"CSL 8-N (Com.Salas e Lojas)", "CSL 8-A (Com.Salas e Lojas)",
|
| 344 |
+
"CSL 16-N (Com.Salas e Lojas)", "CSL 16-A (Com.Salas e Lojas)", "GI (Galpão Industrial)"],
|
| 345 |
+
value="R 1-N (Res. Unifamiliar)", scale=1)
|
| 346 |
+
data_cub = gr.Textbox(label="Data do CUB (mm/aaaa)", value=ultimo_cub, scale=1)
|
| 347 |
+
fator_cub = gr.Slider(0.5, 2.0, value=1, label="Fator para adequação do CUB",
|
| 348 |
+
info="Para contemplar insumos/serviços que não constam na composição", step=0.1)
|
| 349 |
+
forma_cub = gr.Dropdown(["Utilização CUB para projeto semelhante ao projeto padrão",
|
| 350 |
+
"Utilização CUB para projeto diferente do projeto padrão, com os devidos ajustes"],
|
| 351 |
+
label="Estimativa do custo direto", value="Utilização CUB para projeto semelhante ao projeto padrão")
|
| 352 |
+
with gr.Row():
|
| 353 |
+
bdi = gr.Number(label="BDI (%)", value=22.5, scale=0.5)
|
| 354 |
+
tipo_bdi = gr.Dropdown(["Calculado", "Justificado", "Arbitrado"], label="Tipo de BDI", value="Justificado", scale=1)
|
| 355 |
+
with gr.Row():
|
| 356 |
+
fator_local = gr.Slider(0.5, 1.5, value=1, label="Fator local", info="Atribua um coeficiente de valorização se necessário", step=0.1)
|
| 357 |
+
just_local = gr.Textbox(label="Justificativa para o Fator Local", value="-", info="Justifique tecnicamente o fator")
|
| 358 |
+
|
| 359 |
+
# Cálculo da Depreciação
|
| 360 |
+
with gr.Group():
|
| 361 |
+
with gr.Row():
|
| 362 |
+
tipo = gr.Dropdown(label="Tipologia", choices=["APARTAMENTOS", "BANCOS", "CASAS DE ALVENARIA",
|
| 363 |
+
"CASAS DE MADEIRA", "HOTÉIS", "LOJAS", "TEATROS",
|
| 364 |
+
"ARMAZÉNS", "FÁBRICAS", "CONST. RURAIS", "GARAGENS",
|
| 365 |
+
"EDIFÍCIOS DE ESCRITÓRIOS", "GALPÕES (DEPÓSITOS)", "SILOS"], value="CASAS DE ALVENARIA", scale=1)
|
| 366 |
+
rst_con = gr.Dropdown(label="Estado de conservação", choices=["A - novo", "B - entre novo e regular",
|
| 367 |
+
"C - regular", "D - entre regular e reparos simples",
|
| 368 |
+
"E - reparos simples", "F - entre reparos simples e importantes",
|
| 369 |
+
"G - reparos importantes", "H - entre reparos importantes e sem valor"], value="A - novo", scale=1)
|
| 370 |
+
vr = gr.Dropdown(label="Valor residual", choices=["0", "0.1", "0.2"], value="0", scale=0.5)
|
| 371 |
+
deprec = gr.Dropdown(["Por levantamento do custo de recuperação do bem, para deixá-lo no estado de novo ou caso de bens novos ou projetos hipotéticos",
|
| 372 |
+
"Por métodos técnicos consagrados, considerando-se idade, vida útil e estado de conservação",
|
| 373 |
+
"Arbitrado"],
|
| 374 |
+
label="Depreciação Física",
|
| 375 |
+
info="Forma pela qual a depreciação física foi calculada",
|
| 376 |
+
value="Por métodos técnicos consagrados, considerando-se idade, vida útil e estado de conservação")
|
| 377 |
+
#-------------------------------------------------------#
|
| 378 |
+
button_1 = gr.Button("Calcular Construção")
|
| 379 |
+
#-------------------------------------------------------#
|
| 380 |
+
|
| 381 |
+
# Valor do Terreno
|
| 382 |
+
with gr.Row():
|
| 383 |
+
vt_inp = gr.Number(label="Valor do Terreno", info="Campo não precisade de preenchimento p/ o cálculo do Custo de Reprodução", value=0)
|
| 384 |
+
grau_t = gr.Radio(["Grau III", "Grau II", "Grau I"], info="Escolha o Grau de Fundamentação alcançado pela avaliação do terreno", label="", value="Grau III")
|
| 385 |
+
|
| 386 |
+
# Valor final do imóvel
|
| 387 |
+
fc = gr.Slider(0.1, 2.0, value=1.0, label="FC (Fator de Comercialização)", info="O Fator de Comercialização, também chamado de Terceiro Componente ou Vantagem da Coisa Feita, é definido no item 3.20 da NBR 14653-1:2001: “Fator de comercialização: Razão entre o valor de mercado de um bem e o seu custo de reedição ou de substituição, que pode ser maior ou menor que 1", step=0.1)
|
| 388 |
+
forma_fc = gr.Radio(["Inferido em mercado semelhante", "Justificado", "Arbitrado"], label="", info="Estipule como foi elaborado o FC", value="Justificado")
|
| 389 |
+
|
| 390 |
+
#-------------------------------------------------------#
|
| 391 |
+
button_2 = gr.Button("Calcular Terreno + Construção")
|
| 392 |
+
#-------------------------------------------------------#
|
| 393 |
+
|
| 394 |
+
with gr.Column():
|
| 395 |
+
vi = gr.Textbox(label="Valor Inicial da Contrução")
|
| 396 |
+
cd = gr.Textbox(label="Cálculo da Depreciação")
|
| 397 |
+
vt_out = gr.Textbox(label="Valor estimado para o terreno")
|
| 398 |
+
vf = gr.Textbox(label="Valor final do imóvel")
|
| 399 |
+
la = gr.File(label="Laudo de Avaliação")
|
| 400 |
+
|
| 401 |
+
inputs = [
|
| 402 |
+
area, # Área construída (m²)
|
| 403 |
+
data_r, # Data de referência (mm/aaaa)
|
| 404 |
+
data_c, # Data da construção (mm/aaaa)
|
| 405 |
+
cub, # Tipo de CUB
|
| 406 |
+
data_cub, # Data do CUB (mm/aaaa)
|
| 407 |
+
fator_cub, # Fator para adequação do CUB
|
| 408 |
+
forma_cub, # Estimativa do custo direto
|
| 409 |
+
bdi, # BDI (%)
|
| 410 |
+
tipo_bdi, # Tipo de BDI
|
| 411 |
+
fator_local, # Fator local
|
| 412 |
+
just_local, # Justificativa para o Fator Local
|
| 413 |
+
tipo, # Tipologia
|
| 414 |
+
rst_con, # Estado de conservação
|
| 415 |
+
vr, # Valor residual (0, 0.1 ou 0.2)
|
| 416 |
+
deprec, # Depreciação Física
|
| 417 |
+
vt_inp, # Valor do Terreno
|
| 418 |
+
grau_t, # Grau de Fundamentação alcançado pela avaliação do terreno
|
| 419 |
+
fc, # FC (Fator de Comercialização)
|
| 420 |
+
forma_fc # Forma como foi elaborado o FC
|
| 421 |
+
]
|
| 422 |
+
|
| 423 |
+
outputs = [
|
| 424 |
+
vi, # Valor Inicial da Construção
|
| 425 |
+
cd, # Cálculo da Depreciação
|
| 426 |
+
vt_out, # Valor estimado para o terreno
|
| 427 |
+
vf, # Valor final do imóvel
|
| 428 |
+
la # Laudo de Avaliação (arquivo)
|
| 429 |
+
]
|
| 430 |
+
|
| 431 |
+
button_1.click(calcular_valor_imovel, inputs=inputs, outputs=outputs)
|
| 432 |
+
button_2.click(calcular_valor_imovel, inputs=inputs, outputs=outputs)
|
| 433 |
+
|
| 434 |
+
return locals()
|
| 435 |
+
|
| 436 |
+
# Substituir a tabela pelo c�lculo da deprecia��o
|
modules/ml.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
from sklearn.linear_model import LinearRegression, Ridge, BayesianRidge
|
| 4 |
+
from sklearn.tree import DecisionTreeRegressor
|
| 5 |
+
from sklearn.ensemble import RandomForestRegressor
|
| 6 |
+
from sklearn.svm import SVR
|
| 7 |
+
from sklearn.neural_network import MLPRegressor
|
| 8 |
+
from sklearn.neighbors import KNeighborsRegressor
|
| 9 |
+
from sklearn.metrics import r2_score
|
| 10 |
+
from sklearn.model_selection import train_test_split
|
| 11 |
+
from sklearn.preprocessing import MinMaxScaler
|
| 12 |
+
from .shared_state import state # Estado compartilhado
|
| 13 |
+
import io
|
| 14 |
+
from PIL import Image
|
| 15 |
+
|
| 16 |
+
# Global model state to save the trained model
|
| 17 |
+
global_model = {"model": None, "scaler": None, "columns": None}
|
| 18 |
+
|
| 19 |
+
# Train the model
|
| 20 |
+
def apply_ml(df, var_dep, ml_model_name, test_size):
|
| 21 |
+
if df is None:
|
| 22 |
+
df = state.get('new_df') # Busca o DataFrame no estado compartilhado
|
| 23 |
+
if df is None:
|
| 24 |
+
raise ValueError("Nenhum DataFrame disponível para aplicação.")
|
| 25 |
+
df = df.dropna()
|
| 26 |
+
y = df[var_dep]
|
| 27 |
+
X = df.drop(columns=[var_dep])
|
| 28 |
+
|
| 29 |
+
# Remover a coluna "Índice" se ela existir
|
| 30 |
+
if "Índice" in X.columns:
|
| 31 |
+
X = X.drop(columns=["Índice"])
|
| 32 |
+
|
| 33 |
+
# Normalizar os dados com MinMaxScaler
|
| 34 |
+
scaler = MinMaxScaler()
|
| 35 |
+
X = scaler.fit_transform(X)
|
| 36 |
+
|
| 37 |
+
# Divisão em treino e teste com test_size ajustável
|
| 38 |
+
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=1)
|
| 39 |
+
|
| 40 |
+
# Escolha do modelo
|
| 41 |
+
if ml_model_name == "Linear Regression":
|
| 42 |
+
model = LinearRegression()
|
| 43 |
+
elif ml_model_name == "Ridge Regression":
|
| 44 |
+
model = Ridge(alpha=0.5)
|
| 45 |
+
elif ml_model_name == "Bayesian Ridge":
|
| 46 |
+
model = BayesianRidge()
|
| 47 |
+
elif ml_model_name == "Decision Tree":
|
| 48 |
+
model = DecisionTreeRegressor()
|
| 49 |
+
elif ml_model_name == "Random Forest":
|
| 50 |
+
model = RandomForestRegressor()
|
| 51 |
+
elif ml_model_name == "Support Vector Regression (SVR)":
|
| 52 |
+
model = SVR()
|
| 53 |
+
elif ml_model_name == "Neural Network (MLP)":
|
| 54 |
+
model = MLPRegressor(max_iter=5000, tol=0.1, random_state=1)
|
| 55 |
+
elif ml_model_name == "K-Neighbors Regressor":
|
| 56 |
+
model = KNeighborsRegressor(n_neighbors=5)
|
| 57 |
+
else:
|
| 58 |
+
raise ValueError("Modelo de ML inválido.")
|
| 59 |
+
|
| 60 |
+
# Treinamento e avaliação
|
| 61 |
+
model.fit(X_train, y_train)
|
| 62 |
+
train_r2 = r2_score(y_train, model.predict(X_train))
|
| 63 |
+
test_r2 = r2_score(y_test, model.predict(X_test))
|
| 64 |
+
|
| 65 |
+
print(f"Train R²: {train_r2}, Test R²: {test_r2}")
|
| 66 |
+
|
| 67 |
+
# Save the trained model, scaler, and column names for prediction
|
| 68 |
+
global_model["model"] = model
|
| 69 |
+
global_model["scaler"] = scaler
|
| 70 |
+
global_model["columns"] = df.drop(columns=[var_dep]).columns.tolist()
|
| 71 |
+
|
| 72 |
+
# Gerar o gráfico
|
| 73 |
+
plt.figure(figsize=(6, 4))
|
| 74 |
+
plt.bar(["Treino", "Teste"], [train_r2, test_r2], color=["blue", "orange"])
|
| 75 |
+
plt.title(f"Desempenho do Modelo: {ml_model_name} - Test Size: {test_size}")
|
| 76 |
+
plt.ylabel("R²")
|
| 77 |
+
plt.ylim(0, 1) # Limite entre 0 e 1 para facilitar a visualização
|
| 78 |
+
plt.tight_layout()
|
| 79 |
+
|
| 80 |
+
# Salvar o gráfico em um buffer
|
| 81 |
+
buffer = io.BytesIO()
|
| 82 |
+
plt.savefig(buffer, format='png')
|
| 83 |
+
buffer.seek(0)
|
| 84 |
+
plt.close()
|
| 85 |
+
|
| 86 |
+
# Convert the buffer to a PIL Image
|
| 87 |
+
image = Image.open(buffer)
|
| 88 |
+
|
| 89 |
+
return image
|
| 90 |
+
|
| 91 |
+
# Função para atualizar as opções de variáveis dependentes
|
| 92 |
+
def update_var_dep_dropdown(df):
|
| 93 |
+
if df is None:
|
| 94 |
+
df = state.get('new_df') # Busca o DataFrame no estado compartilhado
|
| 95 |
+
if df is None:
|
| 96 |
+
return gr.update(choices=[])
|
| 97 |
+
return gr.update(choices=df.columns.tolist())
|
| 98 |
+
|
| 99 |
+
def predict_new_values(*inputs):
|
| 100 |
+
if global_model["model"] is None:
|
| 101 |
+
return "O modelo ainda não foi treinado. Execute o modelo primeiro."
|
| 102 |
+
|
| 103 |
+
# Reshape input to match model expectation
|
| 104 |
+
new_data = [float(value) for value in inputs]
|
| 105 |
+
new_data_scaled = global_model["scaler"].transform([new_data])
|
| 106 |
+
|
| 107 |
+
# Predict
|
| 108 |
+
prediction = global_model["model"].predict(new_data_scaled)[0]
|
| 109 |
+
return f"Previsão: {prediction:.4f}"
|
| 110 |
+
|
| 111 |
+
# Função para criar a aba Machine Learning
|
| 112 |
+
def ml_tab(new_df_output):
|
| 113 |
+
with gr.Tab("Machine Learning"):
|
| 114 |
+
var_dep_dropdown = gr.Dropdown(choices=[], label="Variável Dependente")
|
| 115 |
+
ml_model_dropdown = gr.Dropdown(
|
| 116 |
+
choices=[
|
| 117 |
+
"Linear Regression", "Ridge Regression", "Bayesian Ridge",
|
| 118 |
+
"Decision Tree", "Random Forest", "Support Vector Regression (SVR)",
|
| 119 |
+
"Neural Network (MLP)", "K-Neighbors Regressor"
|
| 120 |
+
],
|
| 121 |
+
label="Modelo de Machine Learning"
|
| 122 |
+
)
|
| 123 |
+
test_size_slider = gr.Slider(minimum=0.1, maximum=0.5, step=0.05, value=0.3, label="Tamanho do Teste")
|
| 124 |
+
submit_button = gr.Button("Executar Modelo")
|
| 125 |
+
r2_graph_output = gr.Image(label="Gráfico de Desempenho")
|
| 126 |
+
|
| 127 |
+
# Callback to execute the function
|
| 128 |
+
submit_button.click(
|
| 129 |
+
apply_ml,
|
| 130 |
+
inputs=[new_df_output, var_dep_dropdown, ml_model_dropdown, test_size_slider],
|
| 131 |
+
outputs=[r2_graph_output]
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
# Update dropdown options
|
| 135 |
+
new_df_output.change(update_var_dep_dropdown, inputs=[new_df_output], outputs=[var_dep_dropdown])
|
| 136 |
+
|
| 137 |
+
# Add prediction section
|
| 138 |
+
gr.Markdown("### Previsão de Novos Valores")
|
| 139 |
+
|
| 140 |
+
inputs = []
|
| 141 |
+
if global_model["columns"]: # Check if columns exist
|
| 142 |
+
for col in global_model["columns"]:
|
| 143 |
+
inputs.append(gr.Textbox(label=f"Valor para '{col}'"))
|
| 144 |
+
else:
|
| 145 |
+
gr.Markdown("O modelo ainda não foi treinado. Execute o modelo primeiro para realizar previsões.")
|
| 146 |
+
|
| 147 |
+
predict_button = gr.Button("Prever Valores")
|
| 148 |
+
prediction_output = gr.Textbox(label="Resultado da Previsão")
|
| 149 |
+
|
| 150 |
+
# Predict only if inputs were generated
|
| 151 |
+
if inputs:
|
| 152 |
+
predict_button.click(predict_new_values, inputs=inputs, outputs=prediction_output)
|
| 153 |
+
|
| 154 |
+
return locals()
|
modules/otimiza.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import numpy as np
|
| 3 |
+
from sklearn.linear_model import LinearRegression
|
| 4 |
+
from sklearn.metrics import r2_score
|
| 5 |
+
from itertools import product
|
| 6 |
+
from .shared_state import state # Importa o estado compartilhado
|
| 7 |
+
|
| 8 |
+
# OTIMIZA
|
| 9 |
+
def apply_transformation(data, transformation):
|
| 10 |
+
if transformation == "exp" and (data > 50).any():
|
| 11 |
+
return data
|
| 12 |
+
if transformation == "direct":
|
| 13 |
+
return data
|
| 14 |
+
elif transformation == "inverse":
|
| 15 |
+
return 1 / (data + 0.001)
|
| 16 |
+
elif transformation == "log":
|
| 17 |
+
return np.log(data + 0.001)
|
| 18 |
+
elif transformation == "exp":
|
| 19 |
+
return np.exp(data)
|
| 20 |
+
elif transformation == "square":
|
| 21 |
+
return data ** 2
|
| 22 |
+
|
| 23 |
+
def find_best_transformations(df, var_dep, ignore_dichotomous):
|
| 24 |
+
if df is None:
|
| 25 |
+
df = state.get('new_df') # Busca o DataFrame no estado compartilhado
|
| 26 |
+
if df is None:
|
| 27 |
+
raise ValueError("Nenhum DataFrame disponível para otimização.")
|
| 28 |
+
df = df.dropna()
|
| 29 |
+
y = df[var_dep]
|
| 30 |
+
X = df.drop(columns=[var_dep])
|
| 31 |
+
|
| 32 |
+
# Remover a coluna "Índice" se ela existir
|
| 33 |
+
if "Índice" in X.columns:
|
| 34 |
+
X = X.drop(columns=["Índice"])
|
| 35 |
+
|
| 36 |
+
dichotomous_columns = [col for col in X.columns if set(X[col].unique()).issubset({0, 1})]
|
| 37 |
+
if ignore_dichotomous:
|
| 38 |
+
X = X.drop(columns=dichotomous_columns)
|
| 39 |
+
transformations = ["direct", "inverse", "log", "exp", "square"]
|
| 40 |
+
scores = []
|
| 41 |
+
for y_transformation in transformations:
|
| 42 |
+
y_transformed = apply_transformation(y, y_transformation)
|
| 43 |
+
for transformation_combo in product(transformations, repeat=X.shape[1]):
|
| 44 |
+
X_transformed = X.copy()
|
| 45 |
+
for i, transformation in enumerate(transformation_combo):
|
| 46 |
+
column = X.iloc[:, i]
|
| 47 |
+
X_transformed.iloc[:, i] = apply_transformation(column, transformation)
|
| 48 |
+
model = LinearRegression()
|
| 49 |
+
try:
|
| 50 |
+
model.fit(X_transformed, y_transformed)
|
| 51 |
+
predictions = model.predict(X_transformed)
|
| 52 |
+
score = r2_score(y_transformed, predictions)
|
| 53 |
+
scores.append((transformation_combo, y_transformation, score, model))
|
| 54 |
+
except ValueError as e:
|
| 55 |
+
if "Input X contains NaN" in str(e):
|
| 56 |
+
raise ValueError("O conjunto de dados apresenta valores nulos.") from e
|
| 57 |
+
else:
|
| 58 |
+
raise e # Propague outras exceções
|
| 59 |
+
|
| 60 |
+
scores = sorted(scores, key=lambda x: x[2], reverse=True)[:5]
|
| 61 |
+
top_equations = []
|
| 62 |
+
top_transformation_info = []
|
| 63 |
+
top_scores = []
|
| 64 |
+
for combo, y_trans, score, model in scores:
|
| 65 |
+
equation = f"y = {model.intercept_:.4f} " + " ".join(
|
| 66 |
+
[f"{'+' if coef >= 0 else '-'} ({abs(coef):.4f}) * {trans}" for coef, trans in zip(model.coef_, X.columns)]
|
| 67 |
+
)
|
| 68 |
+
transformation_info = {"y": y_trans}
|
| 69 |
+
transformation_info.update(dict(zip(X.columns, combo)))
|
| 70 |
+
top_equations.append([equation])
|
| 71 |
+
top_transformation_info.append(transformation_info)
|
| 72 |
+
top_scores.append([float(score)])
|
| 73 |
+
return top_equations, top_transformation_info, top_scores
|
| 74 |
+
|
| 75 |
+
def update_var_dep_dropdown(df):
|
| 76 |
+
if df is None:
|
| 77 |
+
df = state.get('new_df') # Busca o DataFrame no estado compartilhado
|
| 78 |
+
if df is None:
|
| 79 |
+
return gr.update(choices=[])
|
| 80 |
+
return gr.update(choices=df.columns.tolist())
|
| 81 |
+
|
| 82 |
+
def otimiza_tab(new_df_output):
|
| 83 |
+
with gr.Tab("Otimizar Modelo"):
|
| 84 |
+
var_dep_dropdown = gr.Dropdown(
|
| 85 |
+
choices=[], # Inicialmente vazio
|
| 86 |
+
label="Variável Dependente"
|
| 87 |
+
)
|
| 88 |
+
ignore_dichotomous_checkbox = gr.Checkbox(
|
| 89 |
+
label="Ignorar Variáveis Dicotômicas", value=False
|
| 90 |
+
)
|
| 91 |
+
submit_button = gr.Button("Otimizar variáveis")
|
| 92 |
+
|
| 93 |
+
with gr.Row():
|
| 94 |
+
equations_output = gr.Dataframe(headers=["Equação"], label="Equações (Top 5)")
|
| 95 |
+
with gr.Row():
|
| 96 |
+
transformations_output = gr.JSON(label="Transformações Aplicadas (Top 5)")
|
| 97 |
+
with gr.Row():
|
| 98 |
+
scores_output = gr.Dataframe(headers=["R2_Score"], label="R2_Scores (Top 5)")
|
| 99 |
+
|
| 100 |
+
# Callback para executar a função
|
| 101 |
+
submit_button.click(
|
| 102 |
+
find_best_transformations,
|
| 103 |
+
inputs=[new_df_output, var_dep_dropdown, ignore_dichotomous_checkbox],
|
| 104 |
+
outputs=[equations_output, transformations_output, scores_output]
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
# Atualiza o dropdown de variáveis dependentes quando o DataFrame é atualizado
|
| 108 |
+
new_df_output.change(
|
| 109 |
+
update_var_dep_dropdown,
|
| 110 |
+
inputs=[new_df_output],
|
| 111 |
+
outputs=[var_dep_dropdown]
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
return locals()
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
### Fixar variáveis para efeutar a otimização
|
| 118 |
+
### Trocar o y pelo nome da variável dependente
|
| 119 |
+
### Exibir resultados estatísticos simples (como o SISREG)
|
| 120 |
+
### Exportar para o ML e RL
|
modules/planilha.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import pandas as pd
|
| 3 |
+
from modules.utils import create_new_dataframe_with_index_and_value_unit # Importe a função necessária
|
| 4 |
+
from .shared_state import state # Importa o estado compartilhado
|
| 5 |
+
|
| 6 |
+
# Variável global para armazenar o DataFrame original
|
| 7 |
+
original_df = None
|
| 8 |
+
|
| 9 |
+
# PLANILHA
|
| 10 |
+
def list_sheets(file):
|
| 11 |
+
if file is None:
|
| 12 |
+
return "Nenhum arquivo carregado.", []
|
| 13 |
+
excel_file = pd.ExcelFile(file.name)
|
| 14 |
+
sheet_names = excel_file.sheet_names
|
| 15 |
+
return "\n".join(sheet_names), sheet_names
|
| 16 |
+
|
| 17 |
+
def load_sheet(file, sheet_name):
|
| 18 |
+
if file is None or not sheet_name:
|
| 19 |
+
return pd.DataFrame({"Erro": ["Carregue um arquivo e selecione uma aba."]}), "", []
|
| 20 |
+
df = pd.read_excel(file.name, sheet_name=sheet_name)
|
| 21 |
+
rows, cols = df.shape
|
| 22 |
+
column_list = df.columns.tolist()
|
| 23 |
+
return df, f"{rows} linhas e {cols} colunas", column_list
|
| 24 |
+
|
| 25 |
+
def update_column_selector(file, sheet_name):
|
| 26 |
+
if file is None or not sheet_name:
|
| 27 |
+
return gr.update(choices=[], value=[])
|
| 28 |
+
df = pd.read_excel(file.name, sheet_name=sheet_name)
|
| 29 |
+
columns = df.columns.tolist()
|
| 30 |
+
return gr.update(choices=columns, value=columns)
|
| 31 |
+
|
| 32 |
+
def select_all_columns(select_all, choices):
|
| 33 |
+
if select_all:
|
| 34 |
+
return gr.update(value=choices)
|
| 35 |
+
else:
|
| 36 |
+
return gr.update(value=[])
|
| 37 |
+
|
| 38 |
+
def toggle_sheet_visibility(view_sheet):
|
| 39 |
+
return gr.update(visible=view_sheet)
|
| 40 |
+
|
| 41 |
+
def toggle_operations_inputs(enable_operations):
|
| 42 |
+
return gr.update(visible=enable_operations)
|
| 43 |
+
|
| 44 |
+
def add_new_variable(file, sheet_name, first_var, operation, second_var, new_var_name):
|
| 45 |
+
if file is None or not sheet_name or not first_var or not second_var or not new_var_name:
|
| 46 |
+
return pd.DataFrame({"Erro": ["Preencha todos os campos necessários."]})
|
| 47 |
+
df = pd.read_excel(file.name, sheet_name=sheet_name)
|
| 48 |
+
if first_var not in df.columns or second_var not in df.columns:
|
| 49 |
+
return pd.DataFrame({"Erro": ["Variáveis selecionadas não existem no DataFrame."]})
|
| 50 |
+
|
| 51 |
+
try:
|
| 52 |
+
if operation == "Adição":
|
| 53 |
+
df[new_var_name] = df[first_var] + df[second_var]
|
| 54 |
+
elif operation == "Subtração":
|
| 55 |
+
df[new_var_name] = df[first_var] - df[second_var]
|
| 56 |
+
elif operation == "Multiplicação":
|
| 57 |
+
df[new_var_name] = df[first_var] * df[second_var]
|
| 58 |
+
elif operation == "Divisão":
|
| 59 |
+
df[new_var_name] = df[first_var] / df[second_var]
|
| 60 |
+
except ZeroDivisionError:
|
| 61 |
+
return pd.DataFrame({"Erro": ["Divisão por zero detectada."]})
|
| 62 |
+
except Exception as e:
|
| 63 |
+
return pd.DataFrame({"Erro": [f"Erro ao realizar a operação: {str(e)}"]})
|
| 64 |
+
|
| 65 |
+
return df
|
| 66 |
+
|
| 67 |
+
def update_variable_choices(file, sheet_name):
|
| 68 |
+
if file is None or not sheet_name:
|
| 69 |
+
return gr.update(choices=[]), gr.update(choices=[])
|
| 70 |
+
df = pd.read_excel(file.name, sheet_name=sheet_name)
|
| 71 |
+
return gr.update(choices=df.columns.tolist()), gr.update(choices=df.columns.tolist())
|
| 72 |
+
|
| 73 |
+
def update_dropdown(file):
|
| 74 |
+
sheet_names_text, sheet_names = list_sheets(file)
|
| 75 |
+
return sheet_names_text, gr.update(choices=sheet_names)
|
| 76 |
+
|
| 77 |
+
def update_columns(file, sheet_name):
|
| 78 |
+
df, info, columns = load_sheet(file, sheet_name)
|
| 79 |
+
return (
|
| 80 |
+
df,
|
| 81 |
+
info,
|
| 82 |
+
columns,
|
| 83 |
+
gr.update(choices=columns, value=[], interactive=True)
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
# Função para restaurar o DataFrame ao estado inicial
|
| 87 |
+
def restore_dataframe():
|
| 88 |
+
global original_df
|
| 89 |
+
if original_df is not None:
|
| 90 |
+
return original_df, None # Retorna o DataFrame e um valor nulo para limpar o download
|
| 91 |
+
return pd.DataFrame(), None # Retorna DataFrame vazio e nenhum arquivo
|
| 92 |
+
|
| 93 |
+
def finalize_dataframe(file, sheet_name, selected_columns, first_var, operation, second_var, new_var_name, add_index):
|
| 94 |
+
if file is None or not sheet_name:
|
| 95 |
+
return pd.DataFrame({"Erro": ["Carregue um arquivo e selecione uma aba."]})
|
| 96 |
+
df = pd.read_excel(file.name, sheet_name=sheet_name)
|
| 97 |
+
|
| 98 |
+
# Adiciona a nova variável, se necessário
|
| 99 |
+
if new_var_name and first_var and second_var:
|
| 100 |
+
try:
|
| 101 |
+
if operation == "Adição":
|
| 102 |
+
df[new_var_name] = df[first_var] + df[second_var]
|
| 103 |
+
elif operation == "Subtração":
|
| 104 |
+
df[new_var_name] = df[first_var] - df[second_var]
|
| 105 |
+
elif operation == "Multiplicação":
|
| 106 |
+
df[new_var_name] = df[first_var] * df[second_var]
|
| 107 |
+
elif operation == "Divisão":
|
| 108 |
+
df[new_var_name] = df[first_var] / df[second_var]
|
| 109 |
+
except ZeroDivisionError:
|
| 110 |
+
return pd.DataFrame({"Erro": ["Divisão por zero detectada."]})
|
| 111 |
+
except Exception as e:
|
| 112 |
+
return pd.DataFrame({"Erro": [f"Erro ao realizar a operação: {str(e)}"]})
|
| 113 |
+
|
| 114 |
+
# Adiciona a nova variável às colunas selecionadas, se existir
|
| 115 |
+
if new_var_name and new_var_name in df.columns:
|
| 116 |
+
selected_columns.append(new_var_name)
|
| 117 |
+
|
| 118 |
+
if selected_columns:
|
| 119 |
+
df = df[selected_columns]
|
| 120 |
+
|
| 121 |
+
# Adiciona índice na primeira coluna, se necessário
|
| 122 |
+
if add_index:
|
| 123 |
+
df.insert(0, 'Índice', range(1, len(df) + 1))
|
| 124 |
+
|
| 125 |
+
# Salvando DataFrame filtrado em arquivo CSV
|
| 126 |
+
file_path = "Planilha_final.xlsx"
|
| 127 |
+
df.to_excel(file_path)
|
| 128 |
+
|
| 129 |
+
return df, file_path
|
| 130 |
+
|
| 131 |
+
def planilha_tab(filtered_df_output):
|
| 132 |
+
# Criação da aba
|
| 133 |
+
with gr.Tab("Carregar Planilha"):
|
| 134 |
+
# Checkbox para escolher entre usar o arquivo da aba anterior ou carregar um novo arquivo
|
| 135 |
+
use_filtered_df = gr.Checkbox(label="Usar arquivo da aba anterior", value=True)
|
| 136 |
+
|
| 137 |
+
# Upload do arquivo Excel
|
| 138 |
+
with gr.Row():
|
| 139 |
+
excel_file = gr.File(label="Carregue sua planilha Excel", file_types=[".xls", ".xlsx"], elem_classes=["small-file-upload"], visible=False)
|
| 140 |
+
|
| 141 |
+
with gr.Row():
|
| 142 |
+
list_button = gr.Button("Listar Abas")
|
| 143 |
+
|
| 144 |
+
# Exibição das abas disponíveis e seleção de aba
|
| 145 |
+
with gr.Row():
|
| 146 |
+
sheet_output = gr.Textbox(label="Abas disponíveis", interactive=False)
|
| 147 |
+
sheet_dropdown = gr.Dropdown(label="Selecione uma aba")
|
| 148 |
+
|
| 149 |
+
# Botão para carregar aba e exibir colunas
|
| 150 |
+
with gr.Row():
|
| 151 |
+
load_button = gr.Button("Carregar Aba")
|
| 152 |
+
|
| 153 |
+
# Informações e exibição de colunas da aba carregada
|
| 154 |
+
with gr.Row():
|
| 155 |
+
sheet_info = gr.Text(label="Informação de Linhas e Colunas")
|
| 156 |
+
sheet_columns = gr.JSON(label="Lista de Colunas", visible=False)
|
| 157 |
+
view_sheet_checkbox = gr.Checkbox(label="Visualizar a planilha", value=False)
|
| 158 |
+
|
| 159 |
+
# Exibição do conteúdo da aba selecionada
|
| 160 |
+
with gr.Row():
|
| 161 |
+
sheet_content = gr.Dataframe(
|
| 162 |
+
label="Conteúdo da Aba Selecionada",
|
| 163 |
+
visible=False
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
# CheckboxGroup para seleção de colunas
|
| 167 |
+
with gr.Row():
|
| 168 |
+
column_selector = gr.CheckboxGroup(
|
| 169 |
+
label="Selecione colunas para análise",
|
| 170 |
+
choices=[], # Inicia vazio
|
| 171 |
+
interactive=True
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
# Checkbox para adicionar índice na primeira coluna
|
| 175 |
+
with gr.Row():
|
| 176 |
+
add_index_checkbox = gr.Checkbox(label="Adicionar índice na primeira coluna", value=False)
|
| 177 |
+
|
| 178 |
+
# Checkbox para habilitar operações com variáveis
|
| 179 |
+
operations_checkbox = gr.Checkbox(label="Operações com variáveis", value=False)
|
| 180 |
+
|
| 181 |
+
# Elementos relacionados às operações, inicialmente invisíveis
|
| 182 |
+
with gr.Row(visible=False) as operations_inputs:
|
| 183 |
+
variable_name_textbox = gr.Textbox(
|
| 184 |
+
label="Nome da nova variável",
|
| 185 |
+
placeholder="Digite o nome da nova variável",
|
| 186 |
+
interactive=True
|
| 187 |
+
)
|
| 188 |
+
first_variable_dropdown = gr.Dropdown(
|
| 189 |
+
label="Selecione a primeira variável",
|
| 190 |
+
choices=[],
|
| 191 |
+
interactive=True
|
| 192 |
+
)
|
| 193 |
+
operation_dropdown = gr.Dropdown(
|
| 194 |
+
label="Selecione a operação",
|
| 195 |
+
choices=["Adição", "Subtração", "Multiplicação", "Divisão"],
|
| 196 |
+
interactive=True
|
| 197 |
+
)
|
| 198 |
+
second_variable_dropdown = gr.Dropdown(
|
| 199 |
+
label="Selecione a segunda variável",
|
| 200 |
+
choices=[],
|
| 201 |
+
interactive=True
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
# Associações de eventos
|
| 205 |
+
use_filtered_df.change(lambda x: gr.update(visible=not x), inputs=[use_filtered_df], outputs=[excel_file])
|
| 206 |
+
list_button.click(update_dropdown, inputs=[excel_file], outputs=[sheet_output, sheet_dropdown])
|
| 207 |
+
load_button.click(update_columns, inputs=[excel_file, sheet_dropdown], outputs=[sheet_content, sheet_info, sheet_columns])
|
| 208 |
+
load_button.click(update_column_selector, inputs=[excel_file, sheet_dropdown], outputs=[column_selector])
|
| 209 |
+
view_sheet_checkbox.change(toggle_sheet_visibility, inputs=[view_sheet_checkbox], outputs=[sheet_content])
|
| 210 |
+
operations_checkbox.change(toggle_operations_inputs, inputs=[operations_checkbox], outputs=[operations_inputs])
|
| 211 |
+
|
| 212 |
+
apply_operations_button = gr.Button("Criar Nova Variável")
|
| 213 |
+
new_df_output = gr.Dataframe(label="Novo DataFrame", interactive=True)
|
| 214 |
+
apply_operations_button.click(add_new_variable, inputs=[excel_file, sheet_dropdown, first_variable_dropdown, operation_dropdown, second_variable_dropdown, variable_name_textbox], outputs=[new_df_output])
|
| 215 |
+
load_button.click(update_variable_choices, inputs=[excel_file, sheet_dropdown], outputs=[first_variable_dropdown, second_variable_dropdown])
|
| 216 |
+
|
| 217 |
+
# Botão para finalizar o DataFrame
|
| 218 |
+
finalize_button = gr.Button("Finalizar DataFrame")
|
| 219 |
+
download_output = gr.File(label="Baixar Novo Dataframe")
|
| 220 |
+
finalize_button.click(finalize_dataframe, inputs=[excel_file, sheet_dropdown, column_selector, first_variable_dropdown, operation_dropdown, second_variable_dropdown, variable_name_textbox, add_index_checkbox], outputs=[new_df_output, download_output])
|
| 221 |
+
|
| 222 |
+
# Adicionar botões "Restaurar" e "Limpar"
|
| 223 |
+
with gr.Row():
|
| 224 |
+
restore_button = gr.Button("Restaurar")
|
| 225 |
+
clear_button = gr.ClearButton(components=[excel_file, sheet_output, sheet_dropdown, sheet_info, sheet_columns, view_sheet_checkbox, sheet_content, column_selector, add_index_checkbox, operations_checkbox, variable_name_textbox, first_variable_dropdown, operation_dropdown, second_variable_dropdown, new_df_output, download_output],
|
| 226 |
+
value="Limpar")
|
| 227 |
+
|
| 228 |
+
# Associações de eventos para os botões "Restaurar"
|
| 229 |
+
restore_button.click(restore_dataframe, outputs=[new_df_output, download_output])
|
| 230 |
+
|
| 231 |
+
# Se a opção "Usar arquivo da aba anterior" estiver marcada, carregue o filtered_df_output
|
| 232 |
+
def load_filtered_df(use_filtered_df, filtered_df_output):
|
| 233 |
+
if use_filtered_df:
|
| 234 |
+
df = pd.DataFrame(filtered_df_output)
|
| 235 |
+
return df, f"{df.shape[0]} linhas e {df.shape[1]} colunas", df.columns.tolist(), df.columns.tolist()
|
| 236 |
+
return None, "", [], []
|
| 237 |
+
|
| 238 |
+
use_filtered_df.change(load_filtered_df, inputs=[use_filtered_df, filtered_df_output], outputs=[sheet_content, sheet_info, column_selector, column_selector])
|
| 239 |
+
|
| 240 |
+
return locals(), new_df_output
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
### Manipulação de colunas
|
| 247 |
+
### Receber planilha da aba dados
|
modules/rl.py
ADDED
|
@@ -0,0 +1,1477 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import numpy as np
|
| 4 |
+
import statsmodels.api as sm
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
import seaborn as sns
|
| 7 |
+
import plotly.graph_objects as go
|
| 8 |
+
import plotly.express as px
|
| 9 |
+
import scipy.stats as stats
|
| 10 |
+
from joblib import dump
|
| 11 |
+
import joblib
|
| 12 |
+
import os
|
| 13 |
+
import locale
|
| 14 |
+
|
| 15 |
+
# Configurar o locale para o Brasil
|
| 16 |
+
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
|
| 17 |
+
|
| 18 |
+
# Função para carregar a planilha de dados
|
| 19 |
+
def carregar_planilha(file):
|
| 20 |
+
try:
|
| 21 |
+
# Carregar o arquivo Excel sem forçar o tipo inicialmente
|
| 22 |
+
df = pd.read_excel(file.name)
|
| 23 |
+
|
| 24 |
+
# Garantir que todas as colunas sejam convertidas para float, ignorando erros
|
| 25 |
+
for col in df.columns:
|
| 26 |
+
df[col] = pd.to_numeric(df[col], errors='coerce') # Converte para número, valores inválidos viram NaN
|
| 27 |
+
|
| 28 |
+
# Substituir NaN por zero ou outro valor padrão, se necessário
|
| 29 |
+
df.fillna(0, inplace=True)
|
| 30 |
+
|
| 31 |
+
# Garantir que os cabeçalhos sejam strings
|
| 32 |
+
df.columns = [str(col) for col in df.columns]
|
| 33 |
+
|
| 34 |
+
# Adicionar índice baseado na posição
|
| 35 |
+
df.insert(0, "Dado", range(1, len(df) + 1))
|
| 36 |
+
|
| 37 |
+
# Arredondar colunas de tipo float para 4 casas decimais
|
| 38 |
+
for col in df.select_dtypes(include=[float]).columns:
|
| 39 |
+
df[col] = df[col].round(4)
|
| 40 |
+
|
| 41 |
+
cabecalhos = list(df.columns)
|
| 42 |
+
return cabecalhos, df
|
| 43 |
+
except Exception as e:
|
| 44 |
+
print(f"Erro ao carregar a planilha: {e}")
|
| 45 |
+
return [], pd.DataFrame()
|
| 46 |
+
|
| 47 |
+
# Função para aplicar a transformação selecionada
|
| 48 |
+
def aplicar_transformacao(df, coluna, transformacao):
|
| 49 |
+
try:
|
| 50 |
+
if transformacao == "1/x":
|
| 51 |
+
return 1 / df[coluna].replace(0, np.nan).fillna(0) # Evitar divisão por zero
|
| 52 |
+
elif transformacao == "ln(x)":
|
| 53 |
+
return np.log(df[coluna].replace(0, np.nan).fillna(0)) # Evitar log de zero
|
| 54 |
+
elif transformacao == "x²":
|
| 55 |
+
return df[coluna] ** 2
|
| 56 |
+
elif transformacao == "exp(x)":
|
| 57 |
+
return np.exp(df[coluna])
|
| 58 |
+
else:
|
| 59 |
+
return df[coluna] # Sem transformação, retorna a coluna original
|
| 60 |
+
except Exception as e:
|
| 61 |
+
return df[coluna] # Retorna original em caso de erro
|
| 62 |
+
|
| 63 |
+
# Função para criar gráficos de dispersão para análise exploratória com transformações
|
| 64 |
+
def grafico_dispersao(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out):
|
| 65 |
+
if df.empty or x_coluna not in df.columns or y_coluna not in df.columns:
|
| 66 |
+
return None # Retornar None se o DataFrame estiver vazio ou as colunas não forem válidas
|
| 67 |
+
|
| 68 |
+
# Copiar o DataFrame para manipulação
|
| 69 |
+
df_grafico = df.copy()
|
| 70 |
+
# Convertendo a entrada manual em uma lista de inteiros
|
| 71 |
+
dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()]
|
| 72 |
+
# Removendo os outliers dos DataFrames
|
| 73 |
+
df_grafico = df_grafico[~df_grafico["Dado"].isin(dados_out)]
|
| 74 |
+
# Aplicar transformações nas colunas x e y
|
| 75 |
+
x = aplicar_transformacao(df_grafico, x_coluna, transformacao_x)
|
| 76 |
+
y = aplicar_transformacao(df_grafico, y_coluna, transformacao_y)
|
| 77 |
+
# Calcular a linha de tendência
|
| 78 |
+
coef = np.polyfit(x, y, 1) # Coeficientes da linha de tendência (linear)
|
| 79 |
+
linha_tendencia = np.poly1d(coef)
|
| 80 |
+
y_tendencia = linha_tendencia(x)
|
| 81 |
+
# Calcular resíduos (distância dos pontos à linha de tendência)
|
| 82 |
+
residuos = np.abs(y - y_tendencia)
|
| 83 |
+
# Normalizar os resíduos para aplicar o colormap
|
| 84 |
+
residuos_norm = (residuos - residuos.min()) / (residuos.max() - residuos.min())
|
| 85 |
+
# Calcular o valor de correlação
|
| 86 |
+
correlacao = np.corrcoef(x, y)[0, 1]
|
| 87 |
+
# Criar o gráfico de dispersão com Plotly
|
| 88 |
+
fig = go.Figure()
|
| 89 |
+
# Adicionar os pontos ao gráfico com colormap
|
| 90 |
+
fig.add_trace(go.Scatter(
|
| 91 |
+
x=x,
|
| 92 |
+
y=y,
|
| 93 |
+
mode='markers',
|
| 94 |
+
marker=dict(
|
| 95 |
+
size=8,
|
| 96 |
+
color=residuos_norm, # Colormap com base nos resíduos
|
| 97 |
+
colorscale='Spectral', # Escolher o esquema de cores (pode ser ajustado)
|
| 98 |
+
showscale=False, # Desabilitar a barra de cores
|
| 99 |
+
),
|
| 100 |
+
text=[f"Índice: {idx}<br>{x_coluna}: {x_val:.2f}<br>{y_coluna}: {y_val:.2f}<br>Resíduo: {resid:.2f}"
|
| 101 |
+
for idx, x_val, y_val, resid in zip(df_grafico["Dado"], x, y, residuos)], # Informações no tooltip
|
| 102 |
+
hoverinfo="text"
|
| 103 |
+
))
|
| 104 |
+
|
| 105 |
+
# Adicionar a linha de tendência ao gráfico
|
| 106 |
+
fig.add_trace(go.Scatter(
|
| 107 |
+
x=x,
|
| 108 |
+
y=y_tendencia,
|
| 109 |
+
mode='lines',
|
| 110 |
+
line=dict(color='darkred', width=2), # Linha de tendência com vermelho escuro
|
| 111 |
+
))
|
| 112 |
+
|
| 113 |
+
# Atualizar o layout do gráfico
|
| 114 |
+
fig.update_layout(
|
| 115 |
+
title=f"Gráfico de Dispersão: {x_coluna} vs {y_coluna}<br>Correlação: {correlacao:.2f}",
|
| 116 |
+
xaxis_title=f"{x_coluna} ({transformacao_x})",
|
| 117 |
+
yaxis_title=f"{y_coluna} ({transformacao_y})",
|
| 118 |
+
template="plotly_white",
|
| 119 |
+
showlegend=False
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
return fig
|
| 123 |
+
|
| 124 |
+
# Função para criar boxplot para análise exploratória com transformações
|
| 125 |
+
def grafico_boxplot(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out):
|
| 126 |
+
if df.empty or y_coluna not in df.columns:
|
| 127 |
+
return None # Retorna None se o DataFrame estiver vazio ou as colunas não forem válidas
|
| 128 |
+
|
| 129 |
+
# Copiar o DataFrame para manipulação
|
| 130 |
+
df_boxplot = df.copy()
|
| 131 |
+
|
| 132 |
+
# Convertendo a entrada manual em uma lista de inteiros
|
| 133 |
+
dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()]
|
| 134 |
+
# Removendo os outliers dos DataFrames
|
| 135 |
+
df_boxplot = df_boxplot[~df_boxplot["Dado"].isin(dados_out)]
|
| 136 |
+
|
| 137 |
+
# Aplicar transformação na coluna Y
|
| 138 |
+
y = aplicar_transformacao(df_boxplot, y_coluna, transformacao_y)
|
| 139 |
+
|
| 140 |
+
# Criar lista de textos personalizados para hover
|
| 141 |
+
hover_text = [
|
| 142 |
+
f"Índice: {idx}<br>{y_coluna}: {y_val:.2f}"
|
| 143 |
+
for idx, y_val in zip(df_boxplot["Dado"], y)
|
| 144 |
+
]
|
| 145 |
+
|
| 146 |
+
# Criar o gráfico de boxplot com Plotly
|
| 147 |
+
fig = go.Figure()
|
| 148 |
+
|
| 149 |
+
# Adicionar os dados ao gráfico
|
| 150 |
+
fig.add_trace(go.Box(
|
| 151 |
+
y=y,
|
| 152 |
+
name=y_coluna,
|
| 153 |
+
boxpoints='all', # Mostra todos os pontos, incluindo outliers
|
| 154 |
+
jitter=0.8, # Espalhamento horizontal dos pontos
|
| 155 |
+
pointpos=-1.8, # Posição relativa dos pontos em relação ao boxplot
|
| 156 |
+
marker=dict(
|
| 157 |
+
color='orange',
|
| 158 |
+
size=8
|
| 159 |
+
),
|
| 160 |
+
line=dict(color='gray'),
|
| 161 |
+
hovertemplate="%{text}", # Personaliza o hover
|
| 162 |
+
text=hover_text # Insere a lista de textos no hover
|
| 163 |
+
))
|
| 164 |
+
|
| 165 |
+
# Atualizar o layout do gráfico
|
| 166 |
+
fig.update_layout(
|
| 167 |
+
title=f"Boxplot da Coluna: {y_coluna} ({transformacao_y})",
|
| 168 |
+
yaxis_title=f"{y_coluna} ({transformacao_y})",
|
| 169 |
+
template="plotly_white",
|
| 170 |
+
showlegend=False
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
return fig
|
| 174 |
+
|
| 175 |
+
# Função para atualizar os dropdowns
|
| 176 |
+
def atualizar_dropdowns(cabeçalhos):
|
| 177 |
+
if cabeçalhos:
|
| 178 |
+
# Remover 'Índice' das opções, se não for relevante para as análises
|
| 179 |
+
cabecalhos_sem_indice = [col for col in cabeçalhos if col != "Dado"]
|
| 180 |
+
dropdown_update_x = gr.update(choices=cabecalhos_sem_indice, value=cabecalhos_sem_indice)
|
| 181 |
+
dropdown_update = gr.update(choices=cabecalhos_sem_indice, value=[])
|
| 182 |
+
return [dropdown_update_x] + [dropdown_update] * 4 + [dropdown_update] # Inclui var_dep
|
| 183 |
+
else:
|
| 184 |
+
dropdown_reset = gr.update(choices=[], value=[])
|
| 185 |
+
return [dropdown_reset] * 5 + [dropdown_reset] # Inclui var_dep
|
| 186 |
+
|
| 187 |
+
# Função para criar os cabeçalhos e colocar os limites por variável sem os outliers
|
| 188 |
+
def criar_dataframe_cabecalhos(cabecalhos, x, ln_x, exp_x, inv_x, quad_x, dados, dados_out, var_dep):
|
| 189 |
+
if dados.empty:
|
| 190 |
+
return pd.DataFrame(), {}
|
| 191 |
+
|
| 192 |
+
# Remover os outliers antes de calcular os limites
|
| 193 |
+
if dados_out:
|
| 194 |
+
dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()]
|
| 195 |
+
dados = dados[~dados["Dado"].isin(dados_out)]
|
| 196 |
+
|
| 197 |
+
# Criar um dicionário com as seleções
|
| 198 |
+
escalas = {
|
| 199 |
+
col: [escala] for col, escala in zip(
|
| 200 |
+
x + ln_x + exp_x + inv_x + quad_x,
|
| 201 |
+
["(x)"] * len(x) + ["ln(x)"] * len(ln_x) + ["exp(x)"] * len(exp_x)
|
| 202 |
+
+ ["1/(x)"] * len(inv_x) + ["(x)²"] * len(quad_x)
|
| 203 |
+
)
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
# Criar o DataFrame base
|
| 207 |
+
df = pd.DataFrame.from_dict(escalas, orient='index', columns=['Escala']).transpose()
|
| 208 |
+
|
| 209 |
+
# Reorganizar as colunas para garantir que 'var_dep' seja a primeira
|
| 210 |
+
if var_dep in df.columns:
|
| 211 |
+
cols = [var_dep] + [col for col in df.columns if col != var_dep]
|
| 212 |
+
df = df[cols]
|
| 213 |
+
|
| 214 |
+
# Calcular as linhas adicionais (máximo, mínimo, média) com duas casas decimais
|
| 215 |
+
limites = {
|
| 216 |
+
"Máximo": dados.max().round(2),
|
| 217 |
+
"Mínimo": dados.min().round(2),
|
| 218 |
+
"Média": dados.mean().round(2)
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
# Adicionar as linhas ao DataFrame
|
| 222 |
+
for label, valores in limites.items():
|
| 223 |
+
linha = {col: valores[col] if col in valores else '' for col in df.columns}
|
| 224 |
+
df = pd.concat([df, pd.DataFrame([linha], index=[label])])
|
| 225 |
+
|
| 226 |
+
return df, escalas
|
| 227 |
+
|
| 228 |
+
# Função para criar DataFrames original, escalado, correlação e outliers
|
| 229 |
+
def criar_dataframes(dados, x, ln_x, exp_x, inv_x, quad_x, dados_out, var_dep):
|
| 230 |
+
if dados.empty:
|
| 231 |
+
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), None
|
| 232 |
+
|
| 233 |
+
# Selecionar as colunas escolhidas e incluir "Dado" como identificador
|
| 234 |
+
colunas_escolhidas = x + ln_x + exp_x + inv_x + quad_x
|
| 235 |
+
df_original = dados[["Dado"] + colunas_escolhidas] # Incluindo "Dado"
|
| 236 |
+
|
| 237 |
+
# Aplicar escalas e manter "Dado"
|
| 238 |
+
df_escalado = pd.DataFrame()
|
| 239 |
+
df_escalado["Dado"] = dados["Dado"] # Preservar "Dado" no escalado
|
| 240 |
+
|
| 241 |
+
for col in x:
|
| 242 |
+
df_escalado[col] = dados[col] # (x)
|
| 243 |
+
for col in ln_x:
|
| 244 |
+
df_escalado[col] = np.log(dados[col].replace(0, np.nan)).fillna(0) # ln(x), evitando log de zero
|
| 245 |
+
for col in exp_x:
|
| 246 |
+
df_escalado[col] = np.exp(dados[col]) # exp(x)
|
| 247 |
+
for col in inv_x:
|
| 248 |
+
df_escalado[col] = 1 / dados[col].replace(0, np.nan).fillna(0) # 1/(x), evitando divisão por zero
|
| 249 |
+
for col in quad_x:
|
| 250 |
+
df_escalado[col] = dados[col] ** 2 # (x)²
|
| 251 |
+
|
| 252 |
+
# Reorganizar colunas para que var_dep fique como a segunda coluna
|
| 253 |
+
if var_dep in df_original.columns:
|
| 254 |
+
colunas_reorganizadas_original = (
|
| 255 |
+
["Dado", var_dep] + [col for col in df_original.columns if col not in ["Dado", var_dep]]
|
| 256 |
+
)
|
| 257 |
+
df_original = df_original[colunas_reorganizadas_original]
|
| 258 |
+
|
| 259 |
+
if var_dep in df_escalado.columns:
|
| 260 |
+
colunas_reorganizadas_escalado = (
|
| 261 |
+
["Dado", var_dep] + [col for col in df_escalado.columns if col not in ["Dado", var_dep]]
|
| 262 |
+
)
|
| 263 |
+
df_escalado = df_escalado[colunas_reorganizadas_escalado]
|
| 264 |
+
|
| 265 |
+
# Criando o novo DataFrame com a coluna 'variáveis' sem índice
|
| 266 |
+
df_variaveis = pd.DataFrame({'variáveis': df_original.columns})
|
| 267 |
+
df_variaveis.reset_index(drop=True, inplace=True)
|
| 268 |
+
|
| 269 |
+
# Verificar se dados_out está em branco
|
| 270 |
+
if not dados_out.strip():
|
| 271 |
+
df_outliers = pd.DataFrame() # DataFrame vazio para outliers
|
| 272 |
+
else:
|
| 273 |
+
# Convertendo a entrada manual em uma lista de inteiros
|
| 274 |
+
dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()]
|
| 275 |
+
# Criando o DataFrame de outliers
|
| 276 |
+
df_outliers = df_original[df_original["Dado"].isin(dados_out)] # Filtrar com base nos valores de dados_out
|
| 277 |
+
# Removendo os outliers dos DataFrames originais e escalados
|
| 278 |
+
df_original = df_original[~df_original["Dado"].isin(dados_out)]
|
| 279 |
+
df_escalado = df_escalado[~df_escalado["Dado"].isin(dados_out)]
|
| 280 |
+
|
| 281 |
+
# Criar matriz de correlação
|
| 282 |
+
correlation_matrix = df_escalado.drop(columns=["Dado"]).corr().round(2) # "Dado" não entra na correlação
|
| 283 |
+
|
| 284 |
+
return df_original, df_escalado, df_outliers # , correlation_matrix
|
| 285 |
+
|
| 286 |
+
# Função para Regressão Linear
|
| 287 |
+
def realizar_regressao(var_dep, dados_transformados, df_original, escalas):
|
| 288 |
+
if isinstance(dados_transformados, pd.DataFrame) and not dados_transformados.empty:
|
| 289 |
+
try:
|
| 290 |
+
import statsmodels.api as sm
|
| 291 |
+
import numpy as np
|
| 292 |
+
|
| 293 |
+
if var_dep in dados_transformados.columns:
|
| 294 |
+
# Separando X e y
|
| 295 |
+
y = dados_transformados[var_dep]
|
| 296 |
+
X = dados_transformados.drop(columns=[var_dep, "Dado"])
|
| 297 |
+
|
| 298 |
+
# Número de variáveis independentes
|
| 299 |
+
num_variaveis = X.shape[1]
|
| 300 |
+
|
| 301 |
+
# Adicionando constante
|
| 302 |
+
X = sm.add_constant(X)
|
| 303 |
+
|
| 304 |
+
# Ajustando modelo
|
| 305 |
+
modelo = sm.OLS(y, X).fit()
|
| 306 |
+
|
| 307 |
+
# Inicializar resultados gerais
|
| 308 |
+
resultados_gerais = ""
|
| 309 |
+
|
| 310 |
+
# Estatísticas gerais do modelo
|
| 311 |
+
residuos = modelo.resid
|
| 312 |
+
desvio_padrao_residuos = round(np.std(residuos), 8)
|
| 313 |
+
erro_padronizado = np.round(residuos / desvio_padrao_residuos, 8)
|
| 314 |
+
estatistica_F = round(modelo.fvalue, 8)
|
| 315 |
+
nivel_significancia = round(modelo.f_pvalue, 8)
|
| 316 |
+
r_squared = round(modelo.rsquared, 8)
|
| 317 |
+
r_squared_adjusted = round(modelo.rsquared_adj, 8)
|
| 318 |
+
num_observacoes = int(round(modelo.nobs, 0))
|
| 319 |
+
coef_correlacao = round(np.sqrt(r_squared), 8)
|
| 320 |
+
|
| 321 |
+
# Comparação com a curva normal de resíduos
|
| 322 |
+
intervalos = [(-1.00, 1.00), (-1.64, 1.64), (-1.96, 1.96)]
|
| 323 |
+
percentuais = []
|
| 324 |
+
for intervalo in intervalos:
|
| 325 |
+
min_intervalo, max_intervalo = intervalo
|
| 326 |
+
count = np.sum((erro_padronizado >= min_intervalo) & (erro_padronizado <= max_intervalo))
|
| 327 |
+
percentual = round(count / len(erro_padronizado) * 100, 0)
|
| 328 |
+
percentuais.append(f"{percentual:.0f}%")
|
| 329 |
+
perc_resid = ", ".join(percentuais)
|
| 330 |
+
|
| 331 |
+
# Teste Kolmogorov-Smirnov (KS)
|
| 332 |
+
ks_test = sm.stats.diagnostic.kstest_normal(residuos)
|
| 333 |
+
ks_test_formatted = tuple(f"{val:.4f}" for val in ks_test)
|
| 334 |
+
|
| 335 |
+
import statsmodels.api as sm
|
| 336 |
+
|
| 337 |
+
# Teste Kolmogorov-Smirnov (KS)
|
| 338 |
+
ks_test = sm.stats.diagnostic.kstest_normal(residuos)
|
| 339 |
+
ks_test_formatted = tuple(f"{val:.4f}" for val in ks_test)
|
| 340 |
+
|
| 341 |
+
# Interpretando o resultado
|
| 342 |
+
estatistica_ks, p_valor_ks = ks_test # Desempacotando os valores
|
| 343 |
+
|
| 344 |
+
if p_valor_ks > 0.05:
|
| 345 |
+
ks_interpretacao = "Não há evidências estatísticas suficientes para rejeitar a hipótese nula. Os resíduos podem seguir uma distribuição normal."
|
| 346 |
+
else:
|
| 347 |
+
ks_interpretacao = "Rejeitamos a hipótese nula. Há evidências estatísticas de que os resíduos não seguem uma distribuição normal."
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
# Distância de Cook
|
| 351 |
+
influencia = modelo.get_influence()
|
| 352 |
+
distancia_cook = influencia.cooks_distance[0]
|
| 353 |
+
|
| 354 |
+
# Criar DataFrame com resultados por variável
|
| 355 |
+
coeficientes = modelo.params
|
| 356 |
+
erros_padrao = modelo.bse
|
| 357 |
+
t_values = modelo.tvalues
|
| 358 |
+
p_values = modelo.pvalues
|
| 359 |
+
|
| 360 |
+
# Adicionar a coluna Escala com base no dicionário de escalas
|
| 361 |
+
escalas_coluna = [escalas[var][0] if var in escalas else "Nenhuma" for var in coeficientes.index]
|
| 362 |
+
|
| 363 |
+
resultados_vars = pd.DataFrame({
|
| 364 |
+
"Variável": coeficientes.index,
|
| 365 |
+
"Escala": escalas_coluna, # Adicionando a coluna Escala
|
| 366 |
+
"Coeficiente": coeficientes.values.round(4),
|
| 367 |
+
"Erro Padrão": erros_padrao.values.round(4),
|
| 368 |
+
"t-valor": t_values.values.round(4),
|
| 369 |
+
"P>|t|": p_values.values.round(4)
|
| 370 |
+
})
|
| 371 |
+
|
| 372 |
+
# Expressão da equação do modelo com a variável dependente transformada
|
| 373 |
+
if var_dep in escalas:
|
| 374 |
+
escala_y = escalas[var_dep][0] # Obter a escala associada
|
| 375 |
+
if escala_y == "ln(x)":
|
| 376 |
+
y_label_transformada = f"ln({var_dep})"
|
| 377 |
+
ajustar_termo = lambda termo: f"exp({termo})" # Aplicar exponencial
|
| 378 |
+
elif escala_y == "1/(x)":
|
| 379 |
+
y_label_transformada = f"1/({var_dep})"
|
| 380 |
+
ajustar_termo = lambda termo: f"1/({termo})" # Aplicar inverso
|
| 381 |
+
elif escala_y == "(x)²":
|
| 382 |
+
y_label_transformada = f"({var_dep})²"
|
| 383 |
+
ajustar_termo = lambda termo: f"sqrt({termo})" # Aplicar raiz quadrada
|
| 384 |
+
elif escala_y == "exp(x)":
|
| 385 |
+
y_label_transformada = f"exp({var_dep})"
|
| 386 |
+
ajustar_termo = lambda termo: f"ln({termo})" # Aplicar logaritmo natural
|
| 387 |
+
else:
|
| 388 |
+
y_label_transformada = var_dep # Sem transformação
|
| 389 |
+
ajustar_termo = lambda termo: termo # Sem ajuste
|
| 390 |
+
else:
|
| 391 |
+
y_label_transformada = var_dep # Sem transformação
|
| 392 |
+
ajustar_termo = lambda termo: termo # Sem ajuste
|
| 393 |
+
|
| 394 |
+
# Construir os termos da equação
|
| 395 |
+
termos = []
|
| 396 |
+
for var, coef in zip(coeficientes.index, coeficientes.values):
|
| 397 |
+
if var == 'const':
|
| 398 |
+
interseção = f"{coef:.4f}"
|
| 399 |
+
else:
|
| 400 |
+
if var in escalas:
|
| 401 |
+
escala_var = escalas[var][0] # Obter a escala associada
|
| 402 |
+
if escala_var == "ln(x)":
|
| 403 |
+
var_label = f"ln({var})"
|
| 404 |
+
elif escala_var == "1/(x)":
|
| 405 |
+
var_label = f"1/({var})"
|
| 406 |
+
elif escala_var == "(x)²":
|
| 407 |
+
var_label = f"({var})²"
|
| 408 |
+
elif escala_var == "exp(x)":
|
| 409 |
+
var_label = f"exp({var})"
|
| 410 |
+
else:
|
| 411 |
+
var_label = var # Sem transformação
|
| 412 |
+
else:
|
| 413 |
+
var_label = var # Sem transformação
|
| 414 |
+
termos.append(f"{coef:.4f} * {var_label}")
|
| 415 |
+
|
| 416 |
+
# Montar a equação com a variável dependente transformada
|
| 417 |
+
lado_direito = interseção + " + " + " + ".join(termos)
|
| 418 |
+
equacao_transformada = f"{y_label_transformada} = {lado_direito}"
|
| 419 |
+
# Montar a equação com a variável dependente na escala direta
|
| 420 |
+
equacao_revertida = f"{var_dep} = {ajustar_termo(lado_direito)}"
|
| 421 |
+
# Substituir pontos por vírgulas nas equações
|
| 422 |
+
equacao_transformada = equacao_transformada.replace('.', ',')
|
| 423 |
+
equacao_revertida = equacao_revertida.replace('.', ',')
|
| 424 |
+
# Adicionar as duas formas da equação aos resultados gerais
|
| 425 |
+
#resultados_gerais += f"\nEquação do modelo (variável dependente transformada): {equacao_transformada}"
|
| 426 |
+
resultados_gerais += f"\nEquação do modelo (variável dependente na escala direta): {equacao_revertida}"
|
| 427 |
+
|
| 428 |
+
# Classificar variáveis com base nos p-valores
|
| 429 |
+
def classificar(valor):
|
| 430 |
+
if valor > 0.3:
|
| 431 |
+
return "Fora dos critérios"
|
| 432 |
+
elif valor > 0.2:
|
| 433 |
+
return "Grau I"
|
| 434 |
+
elif valor > 0.1:
|
| 435 |
+
return "Grau II"
|
| 436 |
+
else:
|
| 437 |
+
return "Grau III"
|
| 438 |
+
|
| 439 |
+
resultados_vars['Classificação'] = resultados_vars['P>|t|'].apply(classificar)
|
| 440 |
+
|
| 441 |
+
# Determinar grau único considerando todas as variáveis
|
| 442 |
+
def determinar_grau_unico(classificacoes):
|
| 443 |
+
if "Fora dos critérios" in classificacoes:
|
| 444 |
+
return "Fora dos critérios"
|
| 445 |
+
elif "Grau I" in classificacoes:
|
| 446 |
+
return "Grau I"
|
| 447 |
+
elif "Grau II" in classificacoes:
|
| 448 |
+
return "Grau II"
|
| 449 |
+
else:
|
| 450 |
+
return "Grau III"
|
| 451 |
+
|
| 452 |
+
tab5 = determinar_grau_unico(resultados_vars['Classificação'])
|
| 453 |
+
|
| 454 |
+
# Enquadramento na NBR 14.653-2
|
| 455 |
+
# Item 2 da tabela
|
| 456 |
+
if num_observacoes >= 6 * (num_variaveis + 1):
|
| 457 |
+
tab2 = "Grau III"
|
| 458 |
+
elif num_observacoes >= 4 * (num_variaveis + 1):
|
| 459 |
+
tab2 = "Grau II"
|
| 460 |
+
elif num_observacoes >= 3 * (num_variaveis + 1):
|
| 461 |
+
tab2 = "Grau I"
|
| 462 |
+
else:
|
| 463 |
+
tab2 = "Fora dos critérios"
|
| 464 |
+
|
| 465 |
+
# Item 6 da tabela
|
| 466 |
+
if nivel_significancia <= 0.01:
|
| 467 |
+
tab6 = "Grau III"
|
| 468 |
+
elif nivel_significancia <= 0.02:
|
| 469 |
+
tab6 = "Grau II"
|
| 470 |
+
elif nivel_significancia <= 0.05:
|
| 471 |
+
tab6 = "Grau I"
|
| 472 |
+
else:
|
| 473 |
+
tab6 = "Fora dos critérios"
|
| 474 |
+
|
| 475 |
+
# Resultados gerais formatados
|
| 476 |
+
resultados_gerais = f"""
|
| 477 |
+
Desvio Padrão dos Resíduos: {desvio_padrao_residuos}
|
| 478 |
+
Estatística F: {estatistica_F} | Nível de Significância: {nivel_significancia}
|
| 479 |
+
R²: {r_squared} | R² Ajustado: {r_squared_adjusted}
|
| 480 |
+
Correlação: {coef_correlacao}
|
| 481 |
+
Número de Observações: {num_observacoes}
|
| 482 |
+
Número de Variáveis Independentes: {num_variaveis}
|
| 483 |
+
|
| 484 |
+
Fundamentação - Quant. min. dados (Item 2 tab 9.2.1 NBR 14.653-2): {tab2}
|
| 485 |
+
Fundamentação - Signif. Regressores (Item 5 tab 9.2.1 NBR 14.653-2): {tab5}
|
| 486 |
+
Fundamentação - Signif. Modelo (Item 6 tab 9.2.1 NBR 14.653-2): {tab6}
|
| 487 |
+
|
| 488 |
+
Testes de normalidade:
|
| 489 |
+
1) Comparação (curva normal) - Percentuais atingidos: {perc_resid}
|
| 490 |
+
Ideal 68% - aceitável de 64% a 75%
|
| 491 |
+
Ideal 90% - aceitável de 88% a 95%
|
| 492 |
+
Ideal 95% - aceitável de 95% a 100%
|
| 493 |
+
|
| 494 |
+
Teste Kolmogorov-Smirnov: Estatística = {ks_test_formatted[0]}, Valor-p = {ks_test_formatted[1]} - ({ks_interpretacao})
|
| 495 |
+
|
| 496 |
+
Distância de Cook (Máxima): {np.max(distancia_cook):.8f}
|
| 497 |
+
|
| 498 |
+
Equação do modelo: {equacao_revertida}
|
| 499 |
+
"""
|
| 500 |
+
|
| 501 |
+
# Adicionando a coluna de erro padronizado ao df_final
|
| 502 |
+
df_original_res = df_original.copy()
|
| 503 |
+
df_original_res['Erro Padronizado'] = erro_padronizado
|
| 504 |
+
|
| 505 |
+
# Criar DataFrame apenas com os dados cujo erro padronizado é maior que 2
|
| 506 |
+
df_grandes_residuos = df_original_res[abs(df_original_res['Erro Padronizado']) > 2].copy()
|
| 507 |
+
df_grandes_residuos['Erro Abs'] = abs(df_grandes_residuos['Erro Padronizado'])
|
| 508 |
+
|
| 509 |
+
# Listagem de pontos com resíduos > 2
|
| 510 |
+
listagem_grandes_residuos = ", ".join(map(str, df_grandes_residuos.iloc[:, 0].tolist()))
|
| 511 |
+
|
| 512 |
+
# Listagem dos pontos influenciantes
|
| 513 |
+
limite_cook = 1
|
| 514 |
+
pontos_influentes = []
|
| 515 |
+
for i, cook_dist in enumerate(distancia_cook):
|
| 516 |
+
if cook_dist > limite_cook:
|
| 517 |
+
pontos_influentes.append(dados_transformados.iloc[i]["Dado"]) # Usando a primeira coluna como rótulo
|
| 518 |
+
|
| 519 |
+
# Transformando a lista em uma string separada por vírgula
|
| 520 |
+
listagem_pontos_influentes = ", ".join(map(str, pontos_influentes))
|
| 521 |
+
|
| 522 |
+
# Criação de um dataframe para valores previstos
|
| 523 |
+
valores_previstos = modelo.predict(X)
|
| 524 |
+
valores_previstos_trans = valores_previstos.copy()
|
| 525 |
+
# Reverter a escala da variável dependente, se aplicável
|
| 526 |
+
if var_dep in escalas:
|
| 527 |
+
escala_var_dep = escalas[var_dep][0] # Obtém a escala associada
|
| 528 |
+
|
| 529 |
+
if escala_var_dep == "ln(x)":
|
| 530 |
+
valores_previstos = np.exp(valores_previstos) # Reverte ln(x) para x
|
| 531 |
+
elif escala_var_dep == "1/(x)":
|
| 532 |
+
valores_previstos = 1 / valores_previstos # Reverte 1/(x) para x
|
| 533 |
+
elif escala_var_dep == "(x)²":
|
| 534 |
+
valores_previstos = np.sqrt(valores_previstos) # Reverte (x)² para x
|
| 535 |
+
elif escala_var_dep == "exp(x)":
|
| 536 |
+
valores_previstos = np.log(valores_previstos) # Reverte exp(x) para x
|
| 537 |
+
# Caso não seja necessário reverter, mantém os valores ajustados como estão
|
| 538 |
+
|
| 539 |
+
# Adicionando os valores ajustados como uma nova coluna ao DataFrame original
|
| 540 |
+
df_calc_obs = df_original.copy()
|
| 541 |
+
df_calc_obs['Valores Ajustados'] = round(valores_previstos, 8)
|
| 542 |
+
# Resíduo
|
| 543 |
+
df_calc_obs['Resíduo'] = (df_calc_obs[var_dep].replace(0, np.nan) - df_calc_obs['Valores Ajustados']).round(4)
|
| 544 |
+
# Erro padronizado
|
| 545 |
+
df_calc_obs['Erro Padronizado'] = erro_padronizado.round(4)
|
| 546 |
+
# Adicionando a coluna de Erro
|
| 547 |
+
# Certifique-se de evitar divisão por zero
|
| 548 |
+
df_calc_obs['Erro'] = df_calc_obs['Valores Ajustados'] / df_calc_obs[var_dep].replace(0, np.nan)
|
| 549 |
+
# Arredondar os valores da coluna 'Erro' para melhorar a apresentação
|
| 550 |
+
df_calc_obs['Erro'] = df_calc_obs['Erro'].round(4)
|
| 551 |
+
# Criando a coluna de erro percentual
|
| 552 |
+
df_calc_obs['Erro Percentual (%)'] = (abs(df_calc_obs['Erro'] - 1) * 100).round(4)
|
| 553 |
+
|
| 554 |
+
# Adicionando os valores ajustados como uma nova coluna ao DataFrame original
|
| 555 |
+
df_calc_obs_trans = dados_transformados.copy()
|
| 556 |
+
df_calc_obs_trans['Valores Ajustados'] = round(valores_previstos_trans, 8)
|
| 557 |
+
|
| 558 |
+
return resultados_gerais, resultados_vars, df_grandes_residuos, listagem_grandes_residuos, listagem_pontos_influentes, df_calc_obs, df_calc_obs_trans, erro_padronizado, modelo
|
| 559 |
+
else:
|
| 560 |
+
return "Erro: A variável dependente não está nos dados transformados.", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None
|
| 561 |
+
except Exception as e:
|
| 562 |
+
return f"Erro na regressão: {str(e)}", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None
|
| 563 |
+
else:
|
| 564 |
+
return "Erro: Dados transformados inválidos ou vazios.", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None
|
| 565 |
+
|
| 566 |
+
# Função para plotar gráficos do modelo
|
| 567 |
+
def graficos(escala_dependente, df_calc_obs, df_calc_obs_trans, erro_padronizado, var_dep, num_bins=20):
|
| 568 |
+
|
| 569 |
+
# Gráfico 1: Resíduos Padronizados
|
| 570 |
+
# Normalizar os resíduos padronizados para o colormap
|
| 571 |
+
residuos_norm = (np.abs(erro_padronizado) - np.abs(erro_padronizado).min()) / \
|
| 572 |
+
(np.abs(erro_padronizado).max() - np.abs(erro_padronizado).min())
|
| 573 |
+
|
| 574 |
+
fig1 = go.Figure()
|
| 575 |
+
fig1.add_trace(go.Scatter(
|
| 576 |
+
x=df_calc_obs_trans['Valores Ajustados'],
|
| 577 |
+
y=erro_padronizado,
|
| 578 |
+
mode='markers',
|
| 579 |
+
marker=dict(
|
| 580 |
+
size=8,
|
| 581 |
+
color=residuos_norm, # Aplicar o colorscale
|
| 582 |
+
colorscale='Spectral', # Escolher a paleta de cores
|
| 583 |
+
),
|
| 584 |
+
text=df_calc_obs_trans.iloc[:, 0],
|
| 585 |
+
hovertemplate="<b>Índice: %{text}</b><br>Valores Ajustados: %{x:.2f}<br>Resíduos: %{y:.2f}<extra></extra>"
|
| 586 |
+
))
|
| 587 |
+
fig1.add_hline(y=0, line_dash="dash", line_color="black")
|
| 588 |
+
fig1.add_hline(y=2, line_dash="dot", line_color="red")
|
| 589 |
+
fig1.add_hline(y=-2, line_dash="dot", line_color="red")
|
| 590 |
+
fig1.update_layout(
|
| 591 |
+
title="Gráfico de Resíduos Padronizados",
|
| 592 |
+
xaxis_title="Valores Ajustados",
|
| 593 |
+
yaxis_title="Resíduos Padronizados",
|
| 594 |
+
template="plotly_white"
|
| 595 |
+
)
|
| 596 |
+
|
| 597 |
+
# Gráfico 2: Histograma dos Resíduos Padronizados com Curva Normal
|
| 598 |
+
# Calcula média e desvio padrão dos resíduos
|
| 599 |
+
mean_residuos = np.mean(erro_padronizado)
|
| 600 |
+
std_residuos = np.std(erro_padronizado)
|
| 601 |
+
|
| 602 |
+
# Dados para a curva normal
|
| 603 |
+
x_vals = np.linspace(mean_residuos - 4 * std_residuos, mean_residuos + 4 * std_residuos, 500)
|
| 604 |
+
y_vals = stats.norm.pdf(x_vals, mean_residuos, std_residuos) # PDF da curva normal
|
| 605 |
+
|
| 606 |
+
# Criar o histograma (frequência normalizada)
|
| 607 |
+
hist_values, bin_edges = np.histogram(erro_padronizado, bins=num_bins, density=True)
|
| 608 |
+
scale_factor = max(hist_values) / max(y_vals) # Ajustar altura da curva normal
|
| 609 |
+
y_vals_scaled = y_vals * scale_factor
|
| 610 |
+
|
| 611 |
+
# Calcular o valor médio dos resíduos absolutos para cada bin
|
| 612 |
+
bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2
|
| 613 |
+
bin_colors = 1 - (np.abs(bin_centers) - np.abs(bin_centers).min()) / (np.abs(bin_centers).max() - np.abs(bin_centers).min())
|
| 614 |
+
|
| 615 |
+
fig2 = go.Figure()
|
| 616 |
+
fig2.add_trace(go.Bar(
|
| 617 |
+
x=bin_centers, # Centraliza as barras
|
| 618 |
+
y=hist_values,
|
| 619 |
+
width=np.diff(bin_edges),
|
| 620 |
+
marker=dict(
|
| 621 |
+
color=bin_colors, # Aplicar o colorscale
|
| 622 |
+
colorscale='Reds',
|
| 623 |
+
),
|
| 624 |
+
opacity=0.7,
|
| 625 |
+
name='Histograma'
|
| 626 |
+
))
|
| 627 |
+
|
| 628 |
+
# Adicionar a curva normal ajustada
|
| 629 |
+
fig2.add_trace(go.Scatter(
|
| 630 |
+
x=x_vals,
|
| 631 |
+
y=y_vals_scaled,
|
| 632 |
+
mode='lines',
|
| 633 |
+
line=dict(color='red', width=2),
|
| 634 |
+
name='Curva Normal'
|
| 635 |
+
))
|
| 636 |
+
|
| 637 |
+
fig2.update_layout(
|
| 638 |
+
title="Histograma dos Resíduos Padronizados com Curva Normal",
|
| 639 |
+
xaxis_title="Resíduos Padronizados",
|
| 640 |
+
yaxis_title="Frequência Normalizada",
|
| 641 |
+
template="plotly_white",
|
| 642 |
+
)
|
| 643 |
+
|
| 644 |
+
# Gráfico 3: Valores Ajustados vs Preços Observados
|
| 645 |
+
if escala_dependente == "Direta":
|
| 646 |
+
df_graf_ao = df_calc_obs
|
| 647 |
+
else:
|
| 648 |
+
df_graf_ao = df_calc_obs_trans
|
| 649 |
+
|
| 650 |
+
# Definir os eixos
|
| 651 |
+
valores_observados = df_graf_ao.iloc[:, 1] # Segunda coluna do DataFrame
|
| 652 |
+
valores_calculados = df_graf_ao['Valores Ajustados']
|
| 653 |
+
|
| 654 |
+
# Cálculo dos resíduos normalizados
|
| 655 |
+
residuos = np.abs(valores_observados - valores_calculados)
|
| 656 |
+
residuos_norm = (residuos - residuos.min()) / (residuos.max() - residuos.min()) # Normalizar para o colorscale
|
| 657 |
+
|
| 658 |
+
# Ajustar a reta de regressão linear com statsmodels
|
| 659 |
+
X = sm.add_constant(valores_observados) # Adicionar uma constante (intercepto)
|
| 660 |
+
modelo = sm.OLS(valores_calculados, X).fit() # Ajustar o modelo OLS
|
| 661 |
+
x_reta = np.linspace(valores_observados.min(), valores_observados.max(), 100) # Valores de X para a reta
|
| 662 |
+
y_reta = modelo.predict(sm.add_constant(x_reta)) # Predizer Y com base no modelo
|
| 663 |
+
|
| 664 |
+
# Criar o gráfico
|
| 665 |
+
fig3 = go.Figure()
|
| 666 |
+
fig3.add_trace(go.Scatter(
|
| 667 |
+
x=valores_observados,
|
| 668 |
+
y=valores_calculados,
|
| 669 |
+
mode='markers',
|
| 670 |
+
marker=dict(
|
| 671 |
+
size=8,
|
| 672 |
+
color=residuos_norm, # Aplicar o colorscale
|
| 673 |
+
colorscale='Spectral', # Escolher a paleta de cores
|
| 674 |
+
showscale=False
|
| 675 |
+
),
|
| 676 |
+
text=df_graf_ao.iloc[:, 0], # Índice ou outra coluna para hover
|
| 677 |
+
hovertemplate="<b>Índice: %{text}</b><br>Observado: %{x:.2f}<br>Ajustado: %{y:.2f}<extra></extra>",
|
| 678 |
+
showlegend=False # Remover legenda deste trace
|
| 679 |
+
))
|
| 680 |
+
|
| 681 |
+
# Adicionar a reta ajustada (linha de regressão)
|
| 682 |
+
fig3.add_trace(go.Scatter(
|
| 683 |
+
x=x_reta,
|
| 684 |
+
y=y_reta,
|
| 685 |
+
mode="lines",
|
| 686 |
+
line=dict(color="green", dash="solid"),
|
| 687 |
+
name="Reta Ajustada"
|
| 688 |
+
))
|
| 689 |
+
|
| 690 |
+
# Configuração do layout
|
| 691 |
+
fig3.update_layout(
|
| 692 |
+
title="Valores Ajustados vs Preços Observados",
|
| 693 |
+
xaxis_title="Preços Observados",
|
| 694 |
+
yaxis_title="Valores Ajustados",
|
| 695 |
+
template="plotly_white",
|
| 696 |
+
showlegend=False # Remover legenda
|
| 697 |
+
)
|
| 698 |
+
|
| 699 |
+
|
| 700 |
+
|
| 701 |
+
# Gráfico 4: Matriz de Correlações
|
| 702 |
+
corr_matrix = df_calc_obs_trans.drop(columns=["Dado", "Valores Ajustados"], errors='ignore').corr()
|
| 703 |
+
|
| 704 |
+
# Criar o Heatmap diretamente com texto
|
| 705 |
+
fig4 = go.Figure()
|
| 706 |
+
|
| 707 |
+
# Adicionar os valores manualmente como anotação no gráfico
|
| 708 |
+
for i, row in enumerate(corr_matrix.index):
|
| 709 |
+
for j, col in enumerate(corr_matrix.columns):
|
| 710 |
+
# Determinar a cor do texto com base nas condições
|
| 711 |
+
if row == col:
|
| 712 |
+
color = "black" # Preto para a diagonal (mesma variável)
|
| 713 |
+
elif corr_matrix.loc[row, col] > 0.8: # Correlação acima de 0.8
|
| 714 |
+
if row == var_dep or col == var_dep:
|
| 715 |
+
color = "blue" #tr Azul para correlação com var_dep
|
| 716 |
+
else:
|
| 717 |
+
color = "red" # Vermelho para correlação alta entre variáveis independentes
|
| 718 |
+
else:
|
| 719 |
+
color = "black" # Preto para todas as outras correlações
|
| 720 |
+
|
| 721 |
+
# Adicionar o texto no gráfico
|
| 722 |
+
fig4.add_trace(go.Scatter(
|
| 723 |
+
x=[col],
|
| 724 |
+
y=[row],
|
| 725 |
+
text=[f"{corr_matrix.loc[row, col]:.2f}"],
|
| 726 |
+
mode="text",
|
| 727 |
+
textfont=dict(size=12, color=color), # Aplicar a cor
|
| 728 |
+
))
|
| 729 |
+
|
| 730 |
+
# Atualizar o layout
|
| 731 |
+
fig4.update_layout(
|
| 732 |
+
title="Matriz de Correlações",
|
| 733 |
+
xaxis=dict(title="Variáveis", tickmode='array', tickvals=corr_matrix.columns),
|
| 734 |
+
yaxis=dict(title="Variáveis", tickmode='array', tickvals=corr_matrix.index),
|
| 735 |
+
template="plotly_white",
|
| 736 |
+
showlegend=False
|
| 737 |
+
)
|
| 738 |
+
|
| 739 |
+
return fig1, fig2, fig3, fig4
|
| 740 |
+
|
| 741 |
+
# Função para exportar para o excel
|
| 742 |
+
def exportar_para_excel(nome_arquivo, df_planilha, df_infos, df_original, df_escalado, df_outliers, resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans):
|
| 743 |
+
try:
|
| 744 |
+
# Criar um arquivo Excel com múltiplas abas
|
| 745 |
+
with pd.ExcelWriter(nome_arquivo, engine='xlsxwriter') as writer:
|
| 746 |
+
# Adicionar os DataFrames em abas específicas
|
| 747 |
+
df_planilha.to_excel(writer, sheet_name='Dados Originais', index=False)
|
| 748 |
+
df_infos.to_excel(writer, sheet_name='Infos', index=False)
|
| 749 |
+
df_original.to_excel(writer, sheet_name='Dados Modelo', index=False)
|
| 750 |
+
#df_escalado.to_excel(writer, sheet_name='Dados Modelo Transformados', index=False)
|
| 751 |
+
df_outliers.to_excel(writer, sheet_name='Outliers', index=False)
|
| 752 |
+
|
| 753 |
+
# Converter "Resultados Gerais" para um DataFrame formatado
|
| 754 |
+
resultados_lista = [linha.strip() for linha in resultados_gerais.split("\n") if linha.strip()]
|
| 755 |
+
resultados_df = pd.DataFrame({"Descrição": resultados_lista})
|
| 756 |
+
resultados_df.to_excel(writer, sheet_name='Resultados Gerais', index=False)
|
| 757 |
+
|
| 758 |
+
# Resultados por variável
|
| 759 |
+
resultados_vars.to_excel(writer, sheet_name='Resultados Variáveis', index=False)
|
| 760 |
+
|
| 761 |
+
# Valores calculados x observados
|
| 762 |
+
df_calc_obs.to_excel(writer, sheet_name='Calc x Obs', index=False)
|
| 763 |
+
|
| 764 |
+
# Valores calculados transformados
|
| 765 |
+
#df_calc_obs_trans.to_excel(writer, sheet_name='Calculados Transformados', index=False)
|
| 766 |
+
|
| 767 |
+
return f"Arquivo '{nome_arquivo}' criado com sucesso."
|
| 768 |
+
except Exception as e:
|
| 769 |
+
return f"Erro ao criar o arquivo Excel: {str(e)}"
|
| 770 |
+
|
| 771 |
+
# Função para salvar o modelo
|
| 772 |
+
def exportar_modelo_completo_avse(nome_pacote, modelo, resultados_gerais, df_planilha, df_infos, df_original, df_outliers, resultados_vars, df_calc_obs):
|
| 773 |
+
try:
|
| 774 |
+
# Verificar se o nome do pacote está vazio
|
| 775 |
+
if not nome_pacote:
|
| 776 |
+
return "Erro: O nome do arquivo não pode estar vazio."
|
| 777 |
+
|
| 778 |
+
# Garantir que o nome do pacote tenha a extensão .avse
|
| 779 |
+
if not nome_pacote.endswith(".avse"):
|
| 780 |
+
nome_pacote += ".avse"
|
| 781 |
+
|
| 782 |
+
# Empacotar todos os itens em um dicionário
|
| 783 |
+
pacote = {
|
| 784 |
+
"modelo": modelo,
|
| 785 |
+
"Resultados Gerais": resultados_gerais,
|
| 786 |
+
"df_planilha": df_planilha,
|
| 787 |
+
"df_infos": df_infos,
|
| 788 |
+
"df_original": df_original,
|
| 789 |
+
"df_outliers": df_outliers,
|
| 790 |
+
"resultados_vars": resultados_vars,
|
| 791 |
+
"df_calc_obs": df_calc_obs
|
| 792 |
+
}
|
| 793 |
+
|
| 794 |
+
# Salvar o pacote usando joblib
|
| 795 |
+
dump(pacote, nome_pacote)
|
| 796 |
+
return f"Pacote '{nome_pacote}' criado com sucesso."
|
| 797 |
+
except Exception as e:
|
| 798 |
+
return f"Erro ao criar o pacote: {str(e)}"
|
| 799 |
+
|
| 800 |
+
# Função para carregar o modelo
|
| 801 |
+
def carregar_modelo(nome_pacote):
|
| 802 |
+
try:
|
| 803 |
+
# Carregar o pacote salvo
|
| 804 |
+
pacote = joblib.load(nome_pacote)
|
| 805 |
+
|
| 806 |
+
# Recuperar os objetos do pacote
|
| 807 |
+
modelo = pacote.get("modelo", None) # Objeto do modelo salvo
|
| 808 |
+
resultados_gerais = pacote.get("Resultados Gerais", "") # Resultados gerais do modelo
|
| 809 |
+
df_planilha = pacote.get("df_planilha", pd.DataFrame())
|
| 810 |
+
df_infos = pacote.get("df_infos", pd.DataFrame())
|
| 811 |
+
df_original = pacote.get("df_original", pd.DataFrame())
|
| 812 |
+
df_outliers = pacote.get("df_outliers", pd.DataFrame())
|
| 813 |
+
resultados_vars = pacote.get("resultados_vars", pd.DataFrame())
|
| 814 |
+
df_calc_obs = pacote.get("df_calc_obs", pd.DataFrame())
|
| 815 |
+
|
| 816 |
+
# Adicionar uma linha em branco no df_infos se ele não estiver vazio
|
| 817 |
+
if not df_infos.empty:
|
| 818 |
+
df_infos.loc[len(df_infos)] = [None] * len(df_infos.columns)
|
| 819 |
+
|
| 820 |
+
# Transpor o DataFrame
|
| 821 |
+
df_infos_transposed = df_infos.T
|
| 822 |
+
|
| 823 |
+
# Renomear as colunas
|
| 824 |
+
df_infos_transposed.columns = ['Escalas', 'Máximo', 'Mínimo', 'Médio', 'Avaliando']
|
| 825 |
+
|
| 826 |
+
# Adicionar a coluna 'Variáveis' com os nomes das colunas originais
|
| 827 |
+
df_infos_transposed.insert(0, 'Variáveis', df_infos.columns)
|
| 828 |
+
|
| 829 |
+
# Atribuir o valor padrão "------" à célula da primeira linha da última coluna
|
| 830 |
+
df_infos_transposed.iloc[0, -1] = "Variável dependente"
|
| 831 |
+
|
| 832 |
+
# Obter apenas o nome do arquivo, sem o caminho
|
| 833 |
+
nome_arquivo = os.path.basename(nome_pacote)
|
| 834 |
+
|
| 835 |
+
# Criar mensagem de sucesso com o nome do modelo
|
| 836 |
+
mensagem_sucesso = f"Modelo '{nome_arquivo}' carregado com sucesso."
|
| 837 |
+
|
| 838 |
+
# Retornar todos os componentes necessários
|
| 839 |
+
return (
|
| 840 |
+
mensagem_sucesso, # Mensagem de sucesso com o nome do modelo
|
| 841 |
+
modelo, # Modelo carregado para previsões
|
| 842 |
+
resultados_gerais, # Resultados gerais do modelo
|
| 843 |
+
df_planilha, # Dados originais
|
| 844 |
+
df_original, # Dados usados no modelo
|
| 845 |
+
df_outliers, # Outliers identificados
|
| 846 |
+
resultados_vars, # Resultados por variável
|
| 847 |
+
df_calc_obs, # Dados calculados x observados
|
| 848 |
+
df_infos_transposed, # Informações adicionais do modelo
|
| 849 |
+
)
|
| 850 |
+
except Exception as e:
|
| 851 |
+
# Retornar valores padrão em caso de erro
|
| 852 |
+
return (
|
| 853 |
+
"Carregue o modelo antes de clicar no botão VISUALIZAR MODELO",
|
| 854 |
+
None, # Modelo não carregado
|
| 855 |
+
"", # Resultados gerais vazio
|
| 856 |
+
pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
|
| 857 |
+
)
|
| 858 |
+
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
def realizar_previsoes(modelo, tab_infos_carregada):
|
| 862 |
+
try:
|
| 863 |
+
if modelo is None or tab_infos_carregada.empty:
|
| 864 |
+
return "Erro: Modelo não carregado ou tabela de informações vazia.", pd.DataFrame(), pd.DataFrame(), ""
|
| 865 |
+
|
| 866 |
+
# Identificar as escalas das variáveis independentes e dependente
|
| 867 |
+
escalas_independentes = tab_infos_carregada.iloc[1:, 1].to_list() # a partir da 2ª linha da 2ª coluna
|
| 868 |
+
escala_dependente = tab_infos_carregada.iloc[0, 1] # 1ª linha da 2ª coluna
|
| 869 |
+
|
| 870 |
+
# Valores para previsão estão a partir da 2ª linha da 6ª coluna
|
| 871 |
+
valores_entrada = tab_infos_carregada.iloc[1:, 5].values
|
| 872 |
+
|
| 873 |
+
# Converter valores para numéricos, substituindo inválidos por NaN
|
| 874 |
+
valores_entrada = pd.to_numeric(valores_entrada, errors='coerce')
|
| 875 |
+
|
| 876 |
+
# Aplicar escalas às variáveis independentes
|
| 877 |
+
valores_transformados = []
|
| 878 |
+
for valor, escala in zip(valores_entrada, escalas_independentes):
|
| 879 |
+
if np.isnan(valor):
|
| 880 |
+
valores_transformados.append(np.nan) # Manter NaN se o valor for inválido
|
| 881 |
+
elif escala == "ln(x)":
|
| 882 |
+
valores_transformados.append(np.log(valor) if valor > 0 else np.nan) # Evitar log de zero ou negativo
|
| 883 |
+
elif escala == "1/(x)":
|
| 884 |
+
valores_transformados.append(1 / valor if valor != 0 else np.nan) # Evitar divisão por zero
|
| 885 |
+
elif escala == "(x)²":
|
| 886 |
+
valores_transformados.append(valor ** 2)
|
| 887 |
+
elif escala == "exp(x)":
|
| 888 |
+
valores_transformados.append(np.exp(valor))
|
| 889 |
+
else:
|
| 890 |
+
valores_transformados.append(valor)
|
| 891 |
+
|
| 892 |
+
# Transformar em DataFrame para manter o formato necessário
|
| 893 |
+
df_previsao = pd.DataFrame([valores_transformados], columns=tab_infos_carregada.iloc[1:, 0])
|
| 894 |
+
|
| 895 |
+
# Adicionar constante ao modelo para previsão
|
| 896 |
+
X = sm.add_constant(df_previsao, has_constant='add')
|
| 897 |
+
|
| 898 |
+
# Garantir que as colunas em X correspondam às usadas no modelo
|
| 899 |
+
colunas_modelo = modelo.model.exog_names
|
| 900 |
+
X = X.reindex(columns=colunas_modelo, fill_value=0) # Reordenar e preencher colunas faltantes com 0
|
| 901 |
+
|
| 902 |
+
# Verificar se existem valores NaN nos dados transformados
|
| 903 |
+
if X.isnull().values.any():
|
| 904 |
+
return "Erro: Existem valores inválidos após a transformação. Verifique os dados de entrada.", df_previsao, pd.DataFrame(), ""
|
| 905 |
+
|
| 906 |
+
# Realizar a previsão
|
| 907 |
+
previsao_transformada = modelo.predict(X).iloc[0]
|
| 908 |
+
|
| 909 |
+
# Ajustar para a escala direta da variável dependente
|
| 910 |
+
if escala_dependente == "ln(x)":
|
| 911 |
+
previsao_final = np.exp(previsao_transformada)
|
| 912 |
+
elif escala_dependente == "1/(x)":
|
| 913 |
+
previsao_final = 1 / previsao_transformada
|
| 914 |
+
elif escala_dependente == "(x)²":
|
| 915 |
+
previsao_final = np.sqrt(previsao_transformada)
|
| 916 |
+
elif escala_dependente == "exp(x)":
|
| 917 |
+
previsao_final = np.log(previsao_transformada)
|
| 918 |
+
else:
|
| 919 |
+
previsao_final = previsao_transformada
|
| 920 |
+
|
| 921 |
+
# Calcular limites do campo de arbítrio
|
| 922 |
+
limite_inferior_arbitrio = previsao_final * 0.85
|
| 923 |
+
limite_superior_arbitrio = previsao_final * 1.15
|
| 924 |
+
|
| 925 |
+
# Calcular intervalo de confiança de 80%
|
| 926 |
+
intervalos = modelo.get_prediction(X).conf_int(alpha=0.2)
|
| 927 |
+
limite_inferior_ic = intervalos[0, 0] # Acessar diretamente o valor do array
|
| 928 |
+
limite_superior_ic = intervalos[0, 1] # Acessar diretamente o valor do array
|
| 929 |
+
|
| 930 |
+
# Ajustar limites de IC para a escala direta, se necessário
|
| 931 |
+
if escala_dependente == "ln(x)":
|
| 932 |
+
limite_inferior_ic = np.exp(limite_inferior_ic)
|
| 933 |
+
limite_superior_ic = np.exp(limite_superior_ic)
|
| 934 |
+
elif escala_dependente == "1/(x)":
|
| 935 |
+
limite_inferior_ic = 1 / limite_inferior_ic
|
| 936 |
+
limite_superior_ic = 1 / limite_superior_ic
|
| 937 |
+
elif escala_dependente == "(x)²":
|
| 938 |
+
limite_inferior_ic = np.sqrt(limite_inferior_ic)
|
| 939 |
+
limite_superior_ic = np.sqrt(limite_superior_ic)
|
| 940 |
+
elif escala_dependente == "exp(x)":
|
| 941 |
+
limite_inferior_ic = np.log(limite_inferior_ic)
|
| 942 |
+
limite_superior_ic = np.log(limite_superior_ic)
|
| 943 |
+
|
| 944 |
+
# Cálculo da extrapolação
|
| 945 |
+
extrapolacao_info = ""
|
| 946 |
+
valores_ajustados = valores_entrada.copy()
|
| 947 |
+
contagem_extrapolacoes = 0
|
| 948 |
+
fora_dos_criterios = False
|
| 949 |
+
for i, valor in enumerate(valores_entrada):
|
| 950 |
+
maximo = pd.to_numeric(tab_infos_carregada.iloc[i+1, 2], errors='coerce') # Coluna 3 (Mínimo)
|
| 951 |
+
minimo = pd.to_numeric(tab_infos_carregada.iloc[i+1, 3], errors='coerce') # Coluna 4 (Máximo)
|
| 952 |
+
|
| 953 |
+
if not np.isnan(valor):
|
| 954 |
+
if valor < minimo:
|
| 955 |
+
percentual_extrapolacao = ((minimo - valor) / minimo) * -100
|
| 956 |
+
extrapolacao_info += f"Variável '{tab_infos_carregada.iloc[i+1, 0]}' está {percentual_extrapolacao:.2f}% abaixo do mínimo da amostra.\n"
|
| 957 |
+
valores_ajustados[i] = minimo # Ajustar valor para o mínimo
|
| 958 |
+
contagem_extrapolacoes += 1
|
| 959 |
+
if percentual_extrapolacao < -50:
|
| 960 |
+
fora_dos_criterios = True
|
| 961 |
+
elif valor > maximo:
|
| 962 |
+
percentual_extrapolacao = ((valor - maximo) / maximo) * 100
|
| 963 |
+
extrapolacao_info += f"Variável '{tab_infos_carregada.iloc[i+1, 0]}' está {percentual_extrapolacao:.2f}% acima do máximo da amostra.\n"
|
| 964 |
+
valores_ajustados[i] = maximo # Ajustar valor para o máximo
|
| 965 |
+
contagem_extrapolacoes += 1
|
| 966 |
+
if percentual_extrapolacao > 100:
|
| 967 |
+
fora_dos_criterios = True
|
| 968 |
+
|
| 969 |
+
# Criar novos DataFrames para cada variável que extrapolou
|
| 970 |
+
previsoes_extrapoladas = []
|
| 971 |
+
for i, (valor, ajustado) in enumerate(zip(valores_entrada, valores_ajustados)):
|
| 972 |
+
if valor != ajustado:
|
| 973 |
+
valores_transformados_ext = []
|
| 974 |
+
for valor_ext, escala in zip(valores_ajustados, escalas_independentes):
|
| 975 |
+
if np.isnan(valor_ext):
|
| 976 |
+
valores_transformados_ext.append(np.nan)
|
| 977 |
+
elif escala == "ln(x)":
|
| 978 |
+
valores_transformados_ext.append(np.log(valor_ext) if valor_ext > 0 else np.nan)
|
| 979 |
+
elif escala == "1/(x)":
|
| 980 |
+
valores_transformados_ext.append(1 / valor_ext if valor_ext != 0 else np.nan)
|
| 981 |
+
elif escala == "(x)²":
|
| 982 |
+
valores_transformados_ext.append(valor_ext ** 2)
|
| 983 |
+
elif escala == "exp(x)":
|
| 984 |
+
valores_transformados_ext.append(np.exp(valor_ext))
|
| 985 |
+
else:
|
| 986 |
+
valores_transformados_ext.append(valor_ext)
|
| 987 |
+
|
| 988 |
+
df_previsao_ext = pd.DataFrame([valores_transformados_ext], columns=tab_infos_carregada.iloc[1:, 0])
|
| 989 |
+
X_ext = sm.add_constant(df_previsao_ext, has_constant='add')
|
| 990 |
+
X_ext = X_ext.reindex(columns=colunas_modelo, fill_value=0)
|
| 991 |
+
|
| 992 |
+
if X_ext.isnull().values.any():
|
| 993 |
+
continue
|
| 994 |
+
|
| 995 |
+
previsao_transformada_ext = modelo.predict(X_ext).iloc[0]
|
| 996 |
+
|
| 997 |
+
if escala_dependente == "ln(x)":
|
| 998 |
+
previsao_final_ext = np.exp(previsao_transformada_ext)
|
| 999 |
+
elif escala_dependente == "1/(x)":
|
| 1000 |
+
previsao_final_ext = 1 / previsao_transformada_ext
|
| 1001 |
+
elif escala_dependente == "(x)²":
|
| 1002 |
+
previsao_final_ext = np.sqrt(previsao_transformada_ext)
|
| 1003 |
+
elif escala_dependente == "exp(x)":
|
| 1004 |
+
previsao_final_ext = np.log(previsao_transformada_ext)
|
| 1005 |
+
else:
|
| 1006 |
+
previsao_final_ext = previsao_transformada_ext
|
| 1007 |
+
|
| 1008 |
+
previsoes_extrapoladas.append((df_previsao_ext, previsao_final_ext))
|
| 1009 |
+
|
| 1010 |
+
# Determinar a fundamentação da extrapolação
|
| 1011 |
+
if fora_dos_criterios:
|
| 1012 |
+
fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Fora dos critérios"
|
| 1013 |
+
elif contagem_extrapolacoes == 1:
|
| 1014 |
+
fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Grau II"
|
| 1015 |
+
elif contagem_extrapolacoes > 1:
|
| 1016 |
+
fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Grau I"
|
| 1017 |
+
else:
|
| 1018 |
+
fundamentacao = ""
|
| 1019 |
+
|
| 1020 |
+
# Retornar as informações
|
| 1021 |
+
resultado_texto = (
|
| 1022 |
+
f"Valor estimado central: {locale.currency(previsao_final, grouping=True)}\n"
|
| 1023 |
+
"------------------------------------\n"
|
| 1024 |
+
f"Limite inferior do campo de arbítrio (- 15% do Valor estimado central): {locale.currency(limite_inferior_arbitrio, grouping=True)}\n"
|
| 1025 |
+
f"Limite superior do campo de arbítrio (+ 15% do Valor estimado central): {locale.currency(limite_superior_arbitrio, grouping=True)}\n"
|
| 1026 |
+
"------------------------------------\n"
|
| 1027 |
+
f"Limite inferior do Intervalo de Confiança de 80%: {locale.currency(limite_inferior_ic, grouping=True)}\n"
|
| 1028 |
+
f"Limite superior do Intervalo de Confiança de 80%: {locale.currency(limite_superior_ic, grouping=True)}\n"
|
| 1029 |
+
"------------------------------------\n"
|
| 1030 |
+
f"{extrapolacao_info.strip()}\n"
|
| 1031 |
+
f"{fundamentacao}\n"
|
| 1032 |
+
)
|
| 1033 |
+
|
| 1034 |
+
|
| 1035 |
+
resultado_df = df_previsao
|
| 1036 |
+
resultado_df_ext = pd.DataFrame()
|
| 1037 |
+
resultado_texto_ext = ""
|
| 1038 |
+
|
| 1039 |
+
if previsoes_extrapoladas:
|
| 1040 |
+
for df_ext, previsao_ext in previsoes_extrapoladas:
|
| 1041 |
+
resultado_df_ext = df_ext
|
| 1042 |
+
resultado_texto_ext = (
|
| 1043 |
+
f"Valor estimado central (na fronteira amostral): {locale.currency(previsao_ext, grouping=True)}\n"
|
| 1044 |
+
"------------------------------------\n"
|
| 1045 |
+
)
|
| 1046 |
+
|
| 1047 |
+
# Cálculo da variação percentual
|
| 1048 |
+
variacao_percentual = abs((previsao_final - previsao_ext) / previsao_final) * 100
|
| 1049 |
+
|
| 1050 |
+
# Determinar a fundamentação com base na variação percentual
|
| 1051 |
+
if variacao_percentual > 20:
|
| 1052 |
+
fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Fora dos critérios"
|
| 1053 |
+
elif variacao_percentual > 15:
|
| 1054 |
+
fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Grau I"
|
| 1055 |
+
else:
|
| 1056 |
+
fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Grau II"
|
| 1057 |
+
|
| 1058 |
+
resultado_texto_ext += f"{fundamentacao_ext}\n"
|
| 1059 |
+
|
| 1060 |
+
return resultado_df, resultado_texto, resultado_df_ext, resultado_texto_ext
|
| 1061 |
+
|
| 1062 |
+
except Exception as e:
|
| 1063 |
+
return pd.DataFrame(), "Erro ao realizar a previsão: {str(e)}", pd.DataFrame(), ""
|
| 1064 |
+
|
| 1065 |
+
|
| 1066 |
+
#--------------------------------------------INTERFACE-------------------------------------------#
|
| 1067 |
+
|
| 1068 |
+
def rl_tab():
|
| 1069 |
+
# Criação da aba
|
| 1070 |
+
with gr.Tab("Regressão Linear"):
|
| 1071 |
+
planilha_input = gr.File(label="Carregar Planilha", file_types=[".xls", ".xlsx"], elem_classes=["small-file-upload"])
|
| 1072 |
+
|
| 1073 |
+
with gr.Group():
|
| 1074 |
+
|
| 1075 |
+
#---------ESTADOS DE ARMAZENAMENTO-------------#
|
| 1076 |
+
|
| 1077 |
+
# Estado para armazenamentos
|
| 1078 |
+
cabeçalhos_state = gr.State([])
|
| 1079 |
+
dados_state = gr.State(pd.DataFrame())
|
| 1080 |
+
escalas_state = gr.State({})
|
| 1081 |
+
erro_padronizado_state = gr.State([])
|
| 1082 |
+
# Estados para armazenar os gráficos Plotly gerados
|
| 1083 |
+
grafico_residuos_state = gr.State(None)
|
| 1084 |
+
grafico_histograma_state = gr.State(None)
|
| 1085 |
+
grafico_ajustados_state = gr.State(None)
|
| 1086 |
+
matriz_corr_state = gr.State(None)
|
| 1087 |
+
# Estado para armazenar o modelo gerado
|
| 1088 |
+
modelo_state = gr.State(None)
|
| 1089 |
+
|
| 1090 |
+
#---------PLANILHA DE ENTRADA-------------#
|
| 1091 |
+
|
| 1092 |
+
# Checkbox para controlar a visibilidade da planilha carregada
|
| 1093 |
+
mostrar_planilha = gr.Checkbox(label="MOSTRAR PLANILHA CARREGADA e GRÁFICOS PARA ANÁLISE EXPLORATÓRIA (Os gráficos de dispersão e boxplot são atualizados com a retirada de dados)", value=False)
|
| 1094 |
+
tabela_planilha = gr.Dataframe(visible=False, max_height=250, elem_classes=["small span"]) # Oculto por padrão
|
| 1095 |
+
|
| 1096 |
+
#---------GRÁFICOS DE DISPERSÃO-------------#
|
| 1097 |
+
|
| 1098 |
+
# Gráficos de dispersão
|
| 1099 |
+
with gr.Row(equal_height=True):
|
| 1100 |
+
coluna_y_dispersao = gr.Dropdown(label="Eixo Y (Dispersão)", choices=[], interactive=True, visible=False, scale=3)
|
| 1101 |
+
transformacao_y = gr.Dropdown(
|
| 1102 |
+
label="Transformação para Eixo Y",
|
| 1103 |
+
choices=["Nenhuma", "1/x", "ln(x)", "x²", "exp(x)"],
|
| 1104 |
+
value="Nenhuma",
|
| 1105 |
+
interactive=True,
|
| 1106 |
+
visible=False,
|
| 1107 |
+
scale=2
|
| 1108 |
+
)
|
| 1109 |
+
coluna_x_dispersao = gr.Dropdown(label="Eixo X (Dispersão)", choices=[], interactive=True, visible=False, scale=3)
|
| 1110 |
+
transformacao_x = gr.Dropdown(
|
| 1111 |
+
label="Transformação para Eixo X",
|
| 1112 |
+
choices=["Nenhuma", "1/x", "ln(x)", "x²", "exp(x)"],
|
| 1113 |
+
value="Nenhuma",
|
| 1114 |
+
interactive=True,
|
| 1115 |
+
visible=False,
|
| 1116 |
+
scale=2
|
| 1117 |
+
)
|
| 1118 |
+
# Adicionando os gráficos de dispersão e boxplot lado a lado com o mesmo botão
|
| 1119 |
+
botao_dispersao_boxplot = gr.Button("Gerar Gráficos de Dispersão e Boxplot", visible=False)
|
| 1120 |
+
with gr.Row():
|
| 1121 |
+
grafico_dispersao_saida = gr.Plot(label="Gráfico de Dispersão", visible=False, scale=3)
|
| 1122 |
+
grafico_boxplot_saida = gr.Plot(label="Gráfico Boxplot", visible=False, scale=1)
|
| 1123 |
+
|
| 1124 |
+
#---------ESCOLHA DAS VARIÁVEIS-------------#
|
| 1125 |
+
|
| 1126 |
+
# Checkbox para controlar a visibilidade dos dropdowns
|
| 1127 |
+
mostrar_dropdowns = gr.Checkbox(label="VARIÁVEIS E ESCALAS (Variável dependente e variáveis independentes)", value=False, elem_classes="checkbox-yellow")
|
| 1128 |
+
# Dropdown multi-select para cabeçalhos
|
| 1129 |
+
# Título do conjunto
|
| 1130 |
+
with gr.Group():
|
| 1131 |
+
var_ind = gr.Markdown("Selecione as variáveis desejadas no dropdown da escala para a transformação", visible=False) # Adiciona um título ao grupo de dropdowns
|
| 1132 |
+
with gr.Row():
|
| 1133 |
+
colunas_x = gr.Dropdown(label="(Direta: y ou x)", multiselect=True, choices=[], value=[], interactive=True, visible=False)
|
| 1134 |
+
colunas_ln_x = gr.Dropdown(label="Logarítmica: ln(y) ou ln(x)", multiselect=True, choices=[], interactive=True, visible=False)
|
| 1135 |
+
colunas_exp_x = gr.Dropdown(label="Exponencial: exp(y) ou exp(x)", multiselect=True, choices=[], interactive=True, visible=False)
|
| 1136 |
+
colunas_inv_x = gr.Dropdown(label="Inversa: 1/(y) ou 1/(x)", multiselect=True, choices=[], interactive=True, visible=False)
|
| 1137 |
+
colunas_quad_x = gr.Dropdown(label="Quadrática: (x)² ou (y²) ", multiselect=True, choices=[], interactive=True, visible=False)
|
| 1138 |
+
# Escolha da variável dependente
|
| 1139 |
+
var_dep = gr.Dropdown(label="Variável Dependente", multiselect=False, choices=[], interactive=True, visible=False)
|
| 1140 |
+
|
| 1141 |
+
#---------DATAFRAMES-------------#
|
| 1142 |
+
|
| 1143 |
+
with gr.Row(equal_height=True):
|
| 1144 |
+
# Botão para gerar DataFrames
|
| 1145 |
+
botao_gerar_df = gr.Button("Gerar Planilhas", scale=1)
|
| 1146 |
+
# Checkbox para controlar visibilidade dos DataFrames gerados
|
| 1147 |
+
mostrar_dataframes = gr.Checkbox(label="MOSTRAR PLANILHAS (Escalas e limites, Variáveis transformadas e Outliers)", value=False, scale=3)
|
| 1148 |
+
# DataFrames para visualização
|
| 1149 |
+
tabela_cabecalhos = gr.Dataframe(label="Planilha com Cabeçalhos, Escalas, Máximo, Mínimo e Média das variáveis utilizadas", max_height=200, visible=False, elem_classes=["small span"])
|
| 1150 |
+
tabela_original = gr.Dataframe(label="Planilha original com Variáveis Escolhidas", max_height=250, visible=False, elem_classes=["small span"])
|
| 1151 |
+
tabela_escalada = gr.Dataframe(label="Planilha com Variáveis Transformadas", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"])
|
| 1152 |
+
#matriz_correl = gr.Dataframe(label="Matriz de correlações", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"])
|
| 1153 |
+
df_out = gr.Dataframe(label="Outliers", max_height=150, visible=False, interactive=True, headers=None, elem_classes=["small span"])
|
| 1154 |
+
|
| 1155 |
+
#---------MODELO-------------#
|
| 1156 |
+
|
| 1157 |
+
with gr.Row(equal_height=True):
|
| 1158 |
+
# Adicionando botão para executar a regressão
|
| 1159 |
+
botao_regressao = gr.Button("Gerar Modelo", scale=1)
|
| 1160 |
+
# Checkbox para visualizar os resultados
|
| 1161 |
+
mostrar_resultados = gr.Checkbox(label="REGRESSÃO LINEAR (Resultados gerais e por variável, Resíduos > 2 e Pontos Influenciantes, Valores calculados x valores observados)", value=False, scale=3)
|
| 1162 |
+
# Resultados gerais e por variáveis
|
| 1163 |
+
with gr.Row():
|
| 1164 |
+
resultado_geral = gr.Textbox(label="Resultados Gerais", lines=25, interactive=False, visible=False, scale=1)
|
| 1165 |
+
resultado_coef = gr.Dataframe(label="Resultados por Variável", interactive=False, visible=False, scale=1, elem_classes=["small span"])
|
| 1166 |
+
# Resíduos (dataframe e listagem)
|
| 1167 |
+
residuos = gr.Dataframe(label="Resíduos padronizados > 2", interactive=False, max_height=250, visible=False, scale=1, elem_classes=["small span"])
|
| 1168 |
+
with gr.Row():
|
| 1169 |
+
# Resíduos > 2
|
| 1170 |
+
residuos_list = gr.Textbox(label="Listagem resíduos padronizados > 2", lines=2, interactive=False, visible=False, scale=2)
|
| 1171 |
+
# Pontos influenciantes
|
| 1172 |
+
pontos_inf = gr.Textbox(label="Listagem dos Pontos Influenciantes", lines=2, interactive=False, visible=False, scale=1)
|
| 1173 |
+
# Entrada dinâmica
|
| 1174 |
+
entrada_dinamica = gr.Textbox(label="Retirar dados", lines=2, placeholder="Copie e cole os dados da caixa de texto ao lado ou escolha o dado a ser retirado",
|
| 1175 |
+
interactive=True, visible=False, scale=2)
|
| 1176 |
+
|
| 1177 |
+
# Adicionando a visualização para df_calc_obs
|
| 1178 |
+
calc_obs = gr.Dataframe(label="Calculados x Observados", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"])
|
| 1179 |
+
calc_obs_trans = gr.Dataframe(label="Calculados Transformados", max_height=250, visible=False, interactive=True, elem_classes=["small span"])
|
| 1180 |
+
|
| 1181 |
+
#---------GRÁFICOS DO MODELO-------------#
|
| 1182 |
+
|
| 1183 |
+
with gr.Row(equal_height=True):
|
| 1184 |
+
# Adicionar botão para gerar gráficos
|
| 1185 |
+
botao_graficos = gr.Button("Gerar Gráficos", scale=1)
|
| 1186 |
+
# Checkbox para controlar a visibilidade da seção de gráficos do modelo
|
| 1187 |
+
mostrar_graficos = gr.Checkbox(label="GRÁFICOS DO MODELO (Resíduos padronizados, histograma e valores ajustados vs observados, Matriz de Correlações)", value=False, scale=3)
|
| 1188 |
+
# Escolha a escala da variável dependente para o gráfico Ajustados x Observado
|
| 1189 |
+
escala_graf_ao = gr.Dropdown(label="Escolha a escala da variável dependente para o gráfico Valores Ajustados x Observado", choices=["Transformada", "Direta"])
|
| 1190 |
+
|
| 1191 |
+
# Adicionar rádio para selecionar o gráfico
|
| 1192 |
+
grafico_selecao = gr.Radio(
|
| 1193 |
+
choices=["Resíduos Padronizados", "Histograma", "Valores Ajustados vs Observados", "Matriz de Correlações"],
|
| 1194 |
+
label="Selecione o Gráfico",
|
| 1195 |
+
interactive=True,
|
| 1196 |
+
visible=False
|
| 1197 |
+
)
|
| 1198 |
+
|
| 1199 |
+
# Adicionar saída para o gráfico selecionado
|
| 1200 |
+
grafico_saida = gr.Plot(label="Gráfico Selecionado", visible=False)
|
| 1201 |
+
|
| 1202 |
+
#---------EXPORTAR PARA O EXCEL-------------#
|
| 1203 |
+
|
| 1204 |
+
with gr.Row(equal_height=True):
|
| 1205 |
+
botao_exportar_excel = gr.Button("Exportar Planilha")
|
| 1206 |
+
arquivo_excel = gr.File(label="Baixar Excel", interactive=False, visible=False)
|
| 1207 |
+
|
| 1208 |
+
#---------SALVAR MODELO-------------#
|
| 1209 |
+
|
| 1210 |
+
with gr.Row(equal_height=True):
|
| 1211 |
+
# Adicionar campo de entrada para o nome do arquivo
|
| 1212 |
+
nome_arquivo_modelo = gr.Textbox(
|
| 1213 |
+
label="Nome do Arquivo do Modelo",
|
| 1214 |
+
placeholder="Digite o nome do modelo e clique no botão ao lado para salvar",
|
| 1215 |
+
interactive=True,
|
| 1216 |
+
scale=3
|
| 1217 |
+
)
|
| 1218 |
+
# Adicionar botão "SALVAR MODELO" e output para indicar o sucesso/erro
|
| 1219 |
+
botao_salvar_modelo = gr.Button("Salvar Modelo", scale=1)
|
| 1220 |
+
saida_salvar_modelo = gr.Textbox(
|
| 1221 |
+
label="Status do Salvamento do Modelo",
|
| 1222 |
+
interactive=False,
|
| 1223 |
+
visible=False,
|
| 1224 |
+
lines=2
|
| 1225 |
+
)
|
| 1226 |
+
|
| 1227 |
+
#---------CARREGAR MODELO-------------#
|
| 1228 |
+
|
| 1229 |
+
with gr.Row(equal_height=True):
|
| 1230 |
+
# Entrada para carregar o modelo
|
| 1231 |
+
modelo_input = gr.File(label="Carregar Modelo", file_types=[".avse"], elem_classes=["small-file-upload"])
|
| 1232 |
+
# Botão para carregar o modelo
|
| 1233 |
+
botao_carregar_modelo = gr.Button("Visualizar Modelo")
|
| 1234 |
+
# Checkbox abaixo do botão "Visualizar Modelo"
|
| 1235 |
+
mostrar_modelo_checkbox = gr.Checkbox(
|
| 1236 |
+
label="MOSTRAR DETALHES DO MODELO",
|
| 1237 |
+
value=False,
|
| 1238 |
+
elem_classes=["checkbox-yellow"]
|
| 1239 |
+
)
|
| 1240 |
+
# Mensagem de status para aparecer logo abaixo do modelo_input
|
| 1241 |
+
status_carregamento = gr.Textbox(
|
| 1242 |
+
label="Status do Carregamento do Modelo",
|
| 1243 |
+
interactive=False,
|
| 1244 |
+
visible=False, # Certifique-se de que está visível
|
| 1245 |
+
lines=1,
|
| 1246 |
+
elem_classes=["small span"]
|
| 1247 |
+
)
|
| 1248 |
+
|
| 1249 |
+
# TextBox para exibir os resultados gerais
|
| 1250 |
+
resultado_geral_carregado = gr.Textbox(
|
| 1251 |
+
label="Resultados Gerais (Carregados)",
|
| 1252 |
+
interactive=False,
|
| 1253 |
+
visible=False,
|
| 1254 |
+
lines=25, # Ajuste o número de linhas conforme necessário
|
| 1255 |
+
elem_classes=["small span"]
|
| 1256 |
+
)
|
| 1257 |
+
|
| 1258 |
+
# DataFrames para exibição
|
| 1259 |
+
tabela_planilha_carregada = gr.Dataframe(label="Dados Originais", visible=False, max_height=250, elem_classes=["small span"])
|
| 1260 |
+
tabela_original_carregada = gr.Dataframe(label="Dados Modelo", visible=False, max_height=250, elem_classes=["small span"])
|
| 1261 |
+
tabela_outliers_carregada = gr.Dataframe(label="Outliers", visible=False, max_height=250, elem_classes=["small span"])
|
| 1262 |
+
tabela_resultados_vars_carregada = gr.Dataframe(label="Resultados Variáveis", visible=False, max_height=250, elem_classes=["small span"])
|
| 1263 |
+
tabela_calc_obs_carregada = gr.Dataframe(label="Calculados x Observados", visible=False, max_height=250, elem_classes=["small span"])
|
| 1264 |
+
tab_infos_carregada = gr.Dataframe(label="Variáveis, escalas, limites e avaliando", interactive=True, visible=False, elem_classes=["small span"])
|
| 1265 |
+
|
| 1266 |
+
#---------AVALIAÇÃO-------------#
|
| 1267 |
+
|
| 1268 |
+
botao_previsao = gr.Button("Realizar Previsão")
|
| 1269 |
+
tabela_previsao = gr.Dataframe(label="Valores Transformados para Previsão", visible=True, max_height=250)
|
| 1270 |
+
previsao_saida = gr.Textbox(label="Resultado da Previsão", lines=8, interactive=False, visible=True)
|
| 1271 |
+
|
| 1272 |
+
# Adicionar DataFrames para exibir os resultados das previsões ajustadas
|
| 1273 |
+
tabela_previsao_ajustada = gr.Dataframe(label="Valores Transformados para Previsão Ajustada", visible=True, max_height=250)
|
| 1274 |
+
previsao_ajustada_saida = gr.Textbox(label="Resultado da Previsão Ajustada", lines=8, interactive=False, visible=True)
|
| 1275 |
+
|
| 1276 |
+
# 1. CONTROLE DE VISIBILIDADE
|
| 1277 |
+
|
| 1278 |
+
# 1.1. Mostrar ou ocultar a planilha carregada e os gráficos
|
| 1279 |
+
mostrar_planilha.change(
|
| 1280 |
+
lambda visible, dados: [gr.update(visible=visible, value=dados)] * 8,
|
| 1281 |
+
inputs=[mostrar_planilha, dados_state],
|
| 1282 |
+
outputs=[
|
| 1283 |
+
tabela_planilha, coluna_y_dispersao, transformacao_y, coluna_x_dispersao,
|
| 1284 |
+
transformacao_x, botao_dispersao_boxplot,
|
| 1285 |
+
grafico_dispersao_saida, grafico_boxplot_saida
|
| 1286 |
+
]
|
| 1287 |
+
)
|
| 1288 |
+
|
| 1289 |
+
# 1.2. Mostrar ou ocultar os dropdowns para escolha de variáveis transformadas
|
| 1290 |
+
mostrar_dropdowns.change(
|
| 1291 |
+
lambda visible: [gr.update(visible=visible)] * 7,
|
| 1292 |
+
inputs=[mostrar_dropdowns],
|
| 1293 |
+
outputs=[var_ind, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, var_dep]
|
| 1294 |
+
)
|
| 1295 |
+
|
| 1296 |
+
# 1.3. Mostrar ou ocultar os DataFrames gerados (cabeçalhos, escalas, correlação, etc.)
|
| 1297 |
+
mostrar_dataframes.change(
|
| 1298 |
+
lambda visible: [gr.update(visible=visible)] * 3,
|
| 1299 |
+
inputs=[mostrar_dataframes],
|
| 1300 |
+
outputs=[tabela_cabecalhos, tabela_escalada, df_out] #matriz_correl,
|
| 1301 |
+
)
|
| 1302 |
+
|
| 1303 |
+
# 1.4. Mostrar ou ocultar os resultados da regressão (resultados gerais, resíduos, etc.)
|
| 1304 |
+
mostrar_resultados.change(
|
| 1305 |
+
lambda visible: [gr.update(visible=visible)] * 7,
|
| 1306 |
+
inputs=[mostrar_resultados],
|
| 1307 |
+
outputs=[resultado_geral, resultado_coef, residuos, residuos_list, pontos_inf, entrada_dinamica, calc_obs]
|
| 1308 |
+
)
|
| 1309 |
+
|
| 1310 |
+
# 1.5. Mostrar ou ocultar a seção de gráficos e componentes associados
|
| 1311 |
+
mostrar_graficos.change(
|
| 1312 |
+
lambda visible: [gr.update(visible=visible)] * 2,
|
| 1313 |
+
inputs=[mostrar_graficos],
|
| 1314 |
+
outputs=[grafico_selecao, grafico_saida]
|
| 1315 |
+
)
|
| 1316 |
+
|
| 1317 |
+
# 1.6. Mostrar ou ocultar os detalhes do modelo carregado
|
| 1318 |
+
mostrar_modelo_checkbox.change(
|
| 1319 |
+
lambda visible: [gr.update(visible=visible)] * 8,
|
| 1320 |
+
inputs=[mostrar_modelo_checkbox],
|
| 1321 |
+
outputs=[
|
| 1322 |
+
resultado_geral_carregado, tabela_planilha_carregada, tabela_original_carregada,
|
| 1323 |
+
tabela_outliers_carregada, tabela_resultados_vars_carregada, tabela_calc_obs_carregada,
|
| 1324 |
+
tab_infos_carregada, status_carregamento
|
| 1325 |
+
]
|
| 1326 |
+
)
|
| 1327 |
+
|
| 1328 |
+
# 2. CARREGAMENTO DE PLANILHA E ATUALIZAÇÃO DE DADOS
|
| 1329 |
+
|
| 1330 |
+
# 2.1. Carregar a planilha e extrair cabeçalhos e dados
|
| 1331 |
+
planilha_input.change(
|
| 1332 |
+
lambda file: carregar_planilha(file),
|
| 1333 |
+
inputs=[planilha_input],
|
| 1334 |
+
outputs=[cabeçalhos_state, dados_state]
|
| 1335 |
+
)
|
| 1336 |
+
|
| 1337 |
+
# 2.2. Atualizar as opções de dropdown para o gráfico de dispersão ao carregar uma planilha
|
| 1338 |
+
cabeçalhos_state.change(
|
| 1339 |
+
lambda cabecalhos: [gr.update(choices=cabecalhos, value=None)] * 2,
|
| 1340 |
+
inputs=[cabeçalhos_state],
|
| 1341 |
+
outputs=[coluna_y_dispersao, coluna_x_dispersao]
|
| 1342 |
+
)
|
| 1343 |
+
|
| 1344 |
+
# 2.3. Atualizar os dropdowns para transformações e seleção de variáveis dependentes/independentes
|
| 1345 |
+
cabeçalhos_state.change(
|
| 1346 |
+
atualizar_dropdowns,
|
| 1347 |
+
inputs=[cabeçalhos_state],
|
| 1348 |
+
outputs=[colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, var_dep]
|
| 1349 |
+
)
|
| 1350 |
+
|
| 1351 |
+
# 2.4. Atualizar o gráfico exibido com base na seleção do usuário
|
| 1352 |
+
grafico_selecao.change(
|
| 1353 |
+
lambda escala, selecao, fig1, fig2, fig3, fig4: (
|
| 1354 |
+
fig1 if selecao == "Resíduos Padronizados" else
|
| 1355 |
+
(fig2 if selecao == "Histograma" else
|
| 1356 |
+
(fig3 if selecao == "Valores Ajustados vs Observados" else fig4))
|
| 1357 |
+
),
|
| 1358 |
+
inputs=[escala_graf_ao, grafico_selecao, grafico_residuos_state, grafico_histograma_state, grafico_ajustados_state, matriz_corr_state],
|
| 1359 |
+
outputs=[grafico_saida]
|
| 1360 |
+
)
|
| 1361 |
+
|
| 1362 |
+
# 3. BOTÕES
|
| 1363 |
+
# 3.1. Botão para conectar à função de Gerar os gráficos de dispersão e boxplot (GERAR DISPERSÃO E BOXPLOT)
|
| 1364 |
+
botao_dispersao_boxplot.click(
|
| 1365 |
+
lambda df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out: (
|
| 1366 |
+
grafico_dispersao(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out),
|
| 1367 |
+
grafico_boxplot(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out)
|
| 1368 |
+
),
|
| 1369 |
+
inputs=[dados_state, coluna_y_dispersao, transformacao_y, coluna_x_dispersao, transformacao_x, entrada_dinamica],
|
| 1370 |
+
outputs=[grafico_dispersao_saida, grafico_boxplot_saida]
|
| 1371 |
+
)
|
| 1372 |
+
|
| 1373 |
+
# 3.2. Botão para conectar à função de Gerar os dataframes cabeçalhos, escalas e estatísticas básicas (máximo, mínimo, média) e
|
| 1374 |
+
# transformados, escalados e correlação (GERAR PLANILHAS)
|
| 1375 |
+
botao_gerar_df.click(
|
| 1376 |
+
criar_dataframe_cabecalhos,
|
| 1377 |
+
inputs=[cabeçalhos_state, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, dados_state, entrada_dinamica, var_dep],
|
| 1378 |
+
outputs=[tabela_cabecalhos, escalas_state]
|
| 1379 |
+
)
|
| 1380 |
+
|
| 1381 |
+
botao_gerar_df.click(
|
| 1382 |
+
criar_dataframes,
|
| 1383 |
+
inputs=[dados_state, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, entrada_dinamica, var_dep],
|
| 1384 |
+
outputs=[tabela_original, tabela_escalada, df_out] # , matriz_correl
|
| 1385 |
+
)
|
| 1386 |
+
|
| 1387 |
+
# 3.3. Botão para conectar à função de realizar regressão linear com base nas variáveis selecionadas (GERAR MODELO)
|
| 1388 |
+
botao_regressao.click(
|
| 1389 |
+
realizar_regressao,
|
| 1390 |
+
inputs=[var_dep, tabela_escalada, tabela_original, escalas_state],
|
| 1391 |
+
outputs=[resultado_geral, resultado_coef, residuos, residuos_list, pontos_inf, calc_obs, calc_obs_trans,
|
| 1392 |
+
erro_padronizado_state, modelo_state]
|
| 1393 |
+
)
|
| 1394 |
+
|
| 1395 |
+
# 3.4. Botão para conectar à função de Gerar gráficos do modelo (GERAR GRÁFICOS - resíduos, histograma, ajustados vs observados)
|
| 1396 |
+
botao_graficos.click(
|
| 1397 |
+
graficos, # Função que gera os gráficos Plotly
|
| 1398 |
+
inputs=[escala_graf_ao, calc_obs, calc_obs_trans, erro_padronizado_state, var_dep],
|
| 1399 |
+
outputs=[grafico_residuos_state, grafico_histograma_state, grafico_ajustados_state, matriz_corr_state]
|
| 1400 |
+
)
|
| 1401 |
+
|
| 1402 |
+
# 3.5. Botão para conectar à função de exportar para o excel (EXPORTAR PLANILHA)
|
| 1403 |
+
botao_exportar_excel.click(
|
| 1404 |
+
lambda df_planilha, df_infos, df_original, df_escalado, df_out, resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans: (
|
| 1405 |
+
"resultado.xlsx",
|
| 1406 |
+
exportar_para_excel(
|
| 1407 |
+
"resultado.xlsx", df_planilha, df_infos, df_original, df_escalado, df_out,
|
| 1408 |
+
resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans
|
| 1409 |
+
)
|
| 1410 |
+
)[0],
|
| 1411 |
+
inputs=[
|
| 1412 |
+
dados_state, tabela_cabecalhos, tabela_original, tabela_escalada, df_out,
|
| 1413 |
+
resultado_geral, resultado_coef, calc_obs, calc_obs_trans
|
| 1414 |
+
],
|
| 1415 |
+
outputs=[arquivo_excel]
|
| 1416 |
+
)
|
| 1417 |
+
# Atualizar visibilidade do status após clique no botão
|
| 1418 |
+
botao_exportar_excel.click(
|
| 1419 |
+
lambda *args: gr.update(visible=True),
|
| 1420 |
+
inputs=[],
|
| 1421 |
+
outputs=[arquivo_excel]
|
| 1422 |
+
)
|
| 1423 |
+
|
| 1424 |
+
# 3.6. Botão para conectar à função de salvar o modelo (SALVAR MODELO)
|
| 1425 |
+
botao_salvar_modelo.click(
|
| 1426 |
+
lambda nome_arquivo, modelo, resultados_gerais, df_planilha, df_infos, df_original, df_outliers, resultados_vars, df_calc_obs: (
|
| 1427 |
+
exportar_modelo_completo_avse(
|
| 1428 |
+
nome_arquivo, modelo, resultados_gerais, df_planilha, df_infos, df_original,
|
| 1429 |
+
df_outliers, resultados_vars, df_calc_obs
|
| 1430 |
+
)
|
| 1431 |
+
),
|
| 1432 |
+
inputs=[
|
| 1433 |
+
nome_arquivo_modelo, modelo_state, resultado_geral, dados_state, tabela_cabecalhos,
|
| 1434 |
+
tabela_original, df_out, resultado_coef, calc_obs
|
| 1435 |
+
],
|
| 1436 |
+
outputs=[saida_salvar_modelo]
|
| 1437 |
+
)
|
| 1438 |
+
# Atualizar visibilidade do status após clique no botão
|
| 1439 |
+
botao_salvar_modelo.click(
|
| 1440 |
+
lambda: gr.update(visible=True),
|
| 1441 |
+
inputs=[],
|
| 1442 |
+
outputs=[saida_salvar_modelo]
|
| 1443 |
+
)
|
| 1444 |
+
|
| 1445 |
+
# 3.7. Botão para conectar à função de carregamento do modelo (VISUALIZAR MODELO)
|
| 1446 |
+
botao_carregar_modelo.click(
|
| 1447 |
+
carregar_modelo,
|
| 1448 |
+
inputs=[modelo_input],
|
| 1449 |
+
outputs=[
|
| 1450 |
+
status_carregamento, # Mensagem de status aparece logo abaixo da caixa de upload
|
| 1451 |
+
modelo_state, # Estado para armazenar o modelo carregado
|
| 1452 |
+
resultado_geral_carregado,
|
| 1453 |
+
tabela_planilha_carregada, tabela_original_carregada, tabela_outliers_carregada,
|
| 1454 |
+
tabela_resultados_vars_carregada, tabela_calc_obs_carregada, tab_infos_carregada
|
| 1455 |
+
]
|
| 1456 |
+
)
|
| 1457 |
+
|
| 1458 |
+
# 3.8. Botão para conectar à função de realizar previsões (REALIZAR PREVISÃO)
|
| 1459 |
+
botao_previsao.click(
|
| 1460 |
+
realizar_previsoes,
|
| 1461 |
+
inputs=[modelo_state, tab_infos_carregada],
|
| 1462 |
+
outputs=[tabela_previsao, previsao_saida, tabela_previsao_ajustada, previsao_ajustada_saida]
|
| 1463 |
+
)
|
| 1464 |
+
|
| 1465 |
+
return locals()
|
| 1466 |
+
|
| 1467 |
+
|
| 1468 |
+
### Pontos de máximo e mínimo
|
| 1469 |
+
### Colunas com Coordenadas (ter a possibilidade de dizer se há ou não coordenadas para criar uma dispesão espacial)
|
| 1470 |
+
### Endereços
|
| 1471 |
+
### Colunas que possuem valor 0, não podem ser utilizados na ln ou inversa (mensagem de erro?)
|
| 1472 |
+
### Micronumerosidade
|
| 1473 |
+
### Média, mediana e moda
|
| 1474 |
+
### Problema no ordenamento dos dataframes
|
| 1475 |
+
### Implementar avaliação em massa
|
| 1476 |
+
### Implementar relatório em word
|
| 1477 |
+
### Conectar a elaboração do modelo com o carregamento
|
modules/shared_state.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
state = {}
|
| 2 |
+
|
| 3 |
+
|
modules/utils.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
Created on Fri Nov 22 11:36:45 2024
|
| 4 |
+
|
| 5 |
+
@author: fernando.schwartzer
|
| 6 |
+
"""
|
| 7 |
+
import pandas as pd
|
| 8 |
+
from .shared_state import state
|
| 9 |
+
|
| 10 |
+
def create_new_dataframe_with_index_and_value_unit(file, sheet_name, selected_columns, add_index, calculate_unit_value, col_value, col_area):
|
| 11 |
+
if file is None or not sheet_name or not selected_columns:
|
| 12 |
+
return pd.DataFrame({"Erro": ["Carregue um arquivo, selecione uma aba e colunas."]})
|
| 13 |
+
|
| 14 |
+
# Carrega o DataFrame da aba especificada
|
| 15 |
+
df = pd.read_excel(file.name, sheet_name=sheet_name)
|
| 16 |
+
|
| 17 |
+
# Seleciona apenas as colunas especificadas
|
| 18 |
+
new_df = df[selected_columns]
|
| 19 |
+
|
| 20 |
+
# Adiciona um índice se necessário
|
| 21 |
+
if add_index:
|
| 22 |
+
new_df.insert(0, "Índice", range(1, len(new_df) + 1))
|
| 23 |
+
|
| 24 |
+
# Renomeia e formata a coluna de valor total
|
| 25 |
+
if col_value:
|
| 26 |
+
new_df.rename(columns={col_value: "Valor Total"}, inplace=True)
|
| 27 |
+
new_df["Valor Total"] = new_df["Valor Total"].round(2)
|
| 28 |
+
cols = new_df.columns.tolist()
|
| 29 |
+
cols.insert(1, cols.pop(cols.index("Valor Total")))
|
| 30 |
+
new_df = new_df[cols]
|
| 31 |
+
|
| 32 |
+
# Calcula o valor unitário se necessário
|
| 33 |
+
if calculate_unit_value and col_value and col_area:
|
| 34 |
+
try:
|
| 35 |
+
new_df["Valor Unitário"] = (new_df["Valor Total"] / new_df[col_area]).round(2)
|
| 36 |
+
cols = new_df.columns.tolist()
|
| 37 |
+
cols.insert(2, cols.pop(cols.index("Valor Unitário")))
|
| 38 |
+
new_df = new_df[cols]
|
| 39 |
+
except ZeroDivisionError:
|
| 40 |
+
new_df["Valor Unitário"] = "Erro: Divisão por zero"
|
| 41 |
+
except KeyError:
|
| 42 |
+
new_df["Valor Unitário"] = "Erro: Coluna inválida"
|
| 43 |
+
|
| 44 |
+
# Salva o novo DataFrame no estado compartilhado
|
| 45 |
+
new_df.columns = new_df.columns.map(str)
|
| 46 |
+
state['new_df'] = new_df
|
| 47 |
+
return new_df
|