diff --git "a/modules/rl.py" "b/modules/rl.py" --- "a/modules/rl.py" +++ "b/modules/rl.py" @@ -1,1477 +1,1480 @@ -import gradio as gr -import pandas as pd -import numpy as np -import statsmodels.api as sm -import matplotlib.pyplot as plt -import seaborn as sns -import plotly.graph_objects as go -import plotly.express as px -import scipy.stats as stats -from joblib import dump -import joblib -import os -import locale - -# Configurar o locale para o Brasil -locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8') - -# Função para carregar a planilha de dados -def carregar_planilha(file): - try: - # Carregar o arquivo Excel sem forçar o tipo inicialmente - df = pd.read_excel(file.name) - - # Garantir que todas as colunas sejam convertidas para float, ignorando erros - for col in df.columns: - df[col] = pd.to_numeric(df[col], errors='coerce') # Converte para número, valores inválidos viram NaN - - # Substituir NaN por zero ou outro valor padrão, se necessário - df.fillna(0, inplace=True) - - # Garantir que os cabeçalhos sejam strings - df.columns = [str(col) for col in df.columns] - - # Adicionar índice baseado na posição - df.insert(0, "Dado", range(1, len(df) + 1)) - - # Arredondar colunas de tipo float para 4 casas decimais - for col in df.select_dtypes(include=[float]).columns: - df[col] = df[col].round(4) - - cabecalhos = list(df.columns) - return cabecalhos, df - except Exception as e: - print(f"Erro ao carregar a planilha: {e}") - return [], pd.DataFrame() - -# Função para aplicar a transformação selecionada -def aplicar_transformacao(df, coluna, transformacao): - try: - if transformacao == "1/x": - return 1 / df[coluna].replace(0, np.nan).fillna(0) # Evitar divisão por zero - elif transformacao == "ln(x)": - return np.log(df[coluna].replace(0, np.nan).fillna(0)) # Evitar log de zero - elif transformacao == "x²": - return df[coluna] ** 2 - elif transformacao == "exp(x)": - return np.exp(df[coluna]) - else: - return df[coluna] # Sem transformação, retorna a coluna original - except Exception as e: - return df[coluna] # Retorna original em caso de erro - -# Função para criar gráficos de dispersão para análise exploratória com transformações -def grafico_dispersao(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out): - if df.empty or x_coluna not in df.columns or y_coluna not in df.columns: - return None # Retornar None se o DataFrame estiver vazio ou as colunas não forem válidas - - # Copiar o DataFrame para manipulação - df_grafico = df.copy() - # Convertendo a entrada manual em uma lista de inteiros - dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] - # Removendo os outliers dos DataFrames - df_grafico = df_grafico[~df_grafico["Dado"].isin(dados_out)] - # Aplicar transformações nas colunas x e y - x = aplicar_transformacao(df_grafico, x_coluna, transformacao_x) - y = aplicar_transformacao(df_grafico, y_coluna, transformacao_y) - # Calcular a linha de tendência - coef = np.polyfit(x, y, 1) # Coeficientes da linha de tendência (linear) - linha_tendencia = np.poly1d(coef) - y_tendencia = linha_tendencia(x) - # Calcular resíduos (distância dos pontos à linha de tendência) - residuos = np.abs(y - y_tendencia) - # Normalizar os resíduos para aplicar o colormap - residuos_norm = (residuos - residuos.min()) / (residuos.max() - residuos.min()) - # Calcular o valor de correlação - correlacao = np.corrcoef(x, y)[0, 1] - # Criar o gráfico de dispersão com Plotly - fig = go.Figure() - # Adicionar os pontos ao gráfico com colormap - fig.add_trace(go.Scatter( - x=x, - y=y, - mode='markers', - marker=dict( - size=8, - color=residuos_norm, # Colormap com base nos resíduos - colorscale='Spectral', # Escolher o esquema de cores (pode ser ajustado) - showscale=False, # Desabilitar a barra de cores - ), - text=[f"Índice: {idx}
{x_coluna}: {x_val:.2f}
{y_coluna}: {y_val:.2f}
Resíduo: {resid:.2f}" - for idx, x_val, y_val, resid in zip(df_grafico["Dado"], x, y, residuos)], # Informações no tooltip - hoverinfo="text" - )) - - # Adicionar a linha de tendência ao gráfico - fig.add_trace(go.Scatter( - x=x, - y=y_tendencia, - mode='lines', - line=dict(color='darkred', width=2), # Linha de tendência com vermelho escuro - )) - - # Atualizar o layout do gráfico - fig.update_layout( - title=f"Gráfico de Dispersão: {x_coluna} vs {y_coluna}
Correlação: {correlacao:.2f}", - xaxis_title=f"{x_coluna} ({transformacao_x})", - yaxis_title=f"{y_coluna} ({transformacao_y})", - template="plotly_white", - showlegend=False - ) - - return fig - -# Função para criar boxplot para análise exploratória com transformações -def grafico_boxplot(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out): - if df.empty or y_coluna not in df.columns: - return None # Retorna None se o DataFrame estiver vazio ou as colunas não forem válidas - - # Copiar o DataFrame para manipulação - df_boxplot = df.copy() - - # Convertendo a entrada manual em uma lista de inteiros - dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] - # Removendo os outliers dos DataFrames - df_boxplot = df_boxplot[~df_boxplot["Dado"].isin(dados_out)] - - # Aplicar transformação na coluna Y - y = aplicar_transformacao(df_boxplot, y_coluna, transformacao_y) - - # Criar lista de textos personalizados para hover - hover_text = [ - f"Índice: {idx}
{y_coluna}: {y_val:.2f}" - for idx, y_val in zip(df_boxplot["Dado"], y) - ] - - # Criar o gráfico de boxplot com Plotly - fig = go.Figure() - - # Adicionar os dados ao gráfico - fig.add_trace(go.Box( - y=y, - name=y_coluna, - boxpoints='all', # Mostra todos os pontos, incluindo outliers - jitter=0.8, # Espalhamento horizontal dos pontos - pointpos=-1.8, # Posição relativa dos pontos em relação ao boxplot - marker=dict( - color='orange', - size=8 - ), - line=dict(color='gray'), - hovertemplate="%{text}", # Personaliza o hover - text=hover_text # Insere a lista de textos no hover - )) - - # Atualizar o layout do gráfico - fig.update_layout( - title=f"Boxplot da Coluna: {y_coluna} ({transformacao_y})", - yaxis_title=f"{y_coluna} ({transformacao_y})", - template="plotly_white", - showlegend=False - ) - - return fig - -# Função para atualizar os dropdowns -def atualizar_dropdowns(cabeçalhos): - if cabeçalhos: - # Remover 'Índice' das opções, se não for relevante para as análises - cabecalhos_sem_indice = [col for col in cabeçalhos if col != "Dado"] - dropdown_update_x = gr.update(choices=cabecalhos_sem_indice, value=cabecalhos_sem_indice) - dropdown_update = gr.update(choices=cabecalhos_sem_indice, value=[]) - return [dropdown_update_x] + [dropdown_update] * 4 + [dropdown_update] # Inclui var_dep - else: - dropdown_reset = gr.update(choices=[], value=[]) - return [dropdown_reset] * 5 + [dropdown_reset] # Inclui var_dep - -# Função para criar os cabeçalhos e colocar os limites por variável sem os outliers -def criar_dataframe_cabecalhos(cabecalhos, x, ln_x, exp_x, inv_x, quad_x, dados, dados_out, var_dep): - if dados.empty: - return pd.DataFrame(), {} - - # Remover os outliers antes de calcular os limites - if dados_out: - dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] - dados = dados[~dados["Dado"].isin(dados_out)] - - # Criar um dicionário com as seleções - escalas = { - col: [escala] for col, escala in zip( - x + ln_x + exp_x + inv_x + quad_x, - ["(x)"] * len(x) + ["ln(x)"] * len(ln_x) + ["exp(x)"] * len(exp_x) - + ["1/(x)"] * len(inv_x) + ["(x)²"] * len(quad_x) - ) - } - - # Criar o DataFrame base - df = pd.DataFrame.from_dict(escalas, orient='index', columns=['Escala']).transpose() - - # Reorganizar as colunas para garantir que 'var_dep' seja a primeira - if var_dep in df.columns: - cols = [var_dep] + [col for col in df.columns if col != var_dep] - df = df[cols] - - # Calcular as linhas adicionais (máximo, mínimo, média) com duas casas decimais - limites = { - "Máximo": dados.max().round(2), - "Mínimo": dados.min().round(2), - "Média": dados.mean().round(2) - } - - # Adicionar as linhas ao DataFrame - for label, valores in limites.items(): - linha = {col: valores[col] if col in valores else '' for col in df.columns} - df = pd.concat([df, pd.DataFrame([linha], index=[label])]) - - return df, escalas - -# Função para criar DataFrames original, escalado, correlação e outliers -def criar_dataframes(dados, x, ln_x, exp_x, inv_x, quad_x, dados_out, var_dep): - if dados.empty: - return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), None - - # Selecionar as colunas escolhidas e incluir "Dado" como identificador - colunas_escolhidas = x + ln_x + exp_x + inv_x + quad_x - df_original = dados[["Dado"] + colunas_escolhidas] # Incluindo "Dado" - - # Aplicar escalas e manter "Dado" - df_escalado = pd.DataFrame() - df_escalado["Dado"] = dados["Dado"] # Preservar "Dado" no escalado - - for col in x: - df_escalado[col] = dados[col] # (x) - for col in ln_x: - df_escalado[col] = np.log(dados[col].replace(0, np.nan)).fillna(0) # ln(x), evitando log de zero - for col in exp_x: - df_escalado[col] = np.exp(dados[col]) # exp(x) - for col in inv_x: - df_escalado[col] = 1 / dados[col].replace(0, np.nan).fillna(0) # 1/(x), evitando divisão por zero - for col in quad_x: - df_escalado[col] = dados[col] ** 2 # (x)² - - # Reorganizar colunas para que var_dep fique como a segunda coluna - if var_dep in df_original.columns: - colunas_reorganizadas_original = ( - ["Dado", var_dep] + [col for col in df_original.columns if col not in ["Dado", var_dep]] - ) - df_original = df_original[colunas_reorganizadas_original] - - if var_dep in df_escalado.columns: - colunas_reorganizadas_escalado = ( - ["Dado", var_dep] + [col for col in df_escalado.columns if col not in ["Dado", var_dep]] - ) - df_escalado = df_escalado[colunas_reorganizadas_escalado] - - # Criando o novo DataFrame com a coluna 'variáveis' sem índice - df_variaveis = pd.DataFrame({'variáveis': df_original.columns}) - df_variaveis.reset_index(drop=True, inplace=True) - - # Verificar se dados_out está em branco - if not dados_out.strip(): - df_outliers = pd.DataFrame() # DataFrame vazio para outliers - else: - # Convertendo a entrada manual em uma lista de inteiros - dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] - # Criando o DataFrame de outliers - df_outliers = df_original[df_original["Dado"].isin(dados_out)] # Filtrar com base nos valores de dados_out - # Removendo os outliers dos DataFrames originais e escalados - df_original = df_original[~df_original["Dado"].isin(dados_out)] - df_escalado = df_escalado[~df_escalado["Dado"].isin(dados_out)] - - # Criar matriz de correlação - correlation_matrix = df_escalado.drop(columns=["Dado"]).corr().round(2) # "Dado" não entra na correlação - - return df_original, df_escalado, df_outliers # , correlation_matrix - -# Função para Regressão Linear -def realizar_regressao(var_dep, dados_transformados, df_original, escalas): - if isinstance(dados_transformados, pd.DataFrame) and not dados_transformados.empty: - try: - import statsmodels.api as sm - import numpy as np - - if var_dep in dados_transformados.columns: - # Separando X e y - y = dados_transformados[var_dep] - X = dados_transformados.drop(columns=[var_dep, "Dado"]) - - # Número de variáveis independentes - num_variaveis = X.shape[1] - - # Adicionando constante - X = sm.add_constant(X) - - # Ajustando modelo - modelo = sm.OLS(y, X).fit() - - # Inicializar resultados gerais - resultados_gerais = "" - - # Estatísticas gerais do modelo - residuos = modelo.resid - desvio_padrao_residuos = round(np.std(residuos), 8) - erro_padronizado = np.round(residuos / desvio_padrao_residuos, 8) - estatistica_F = round(modelo.fvalue, 8) - nivel_significancia = round(modelo.f_pvalue, 8) - r_squared = round(modelo.rsquared, 8) - r_squared_adjusted = round(modelo.rsquared_adj, 8) - num_observacoes = int(round(modelo.nobs, 0)) - coef_correlacao = round(np.sqrt(r_squared), 8) - - # Comparação com a curva normal de resíduos - intervalos = [(-1.00, 1.00), (-1.64, 1.64), (-1.96, 1.96)] - percentuais = [] - for intervalo in intervalos: - min_intervalo, max_intervalo = intervalo - count = np.sum((erro_padronizado >= min_intervalo) & (erro_padronizado <= max_intervalo)) - percentual = round(count / len(erro_padronizado) * 100, 0) - percentuais.append(f"{percentual:.0f}%") - perc_resid = ", ".join(percentuais) - - # Teste Kolmogorov-Smirnov (KS) - ks_test = sm.stats.diagnostic.kstest_normal(residuos) - ks_test_formatted = tuple(f"{val:.4f}" for val in ks_test) - - import statsmodels.api as sm - - # Teste Kolmogorov-Smirnov (KS) - ks_test = sm.stats.diagnostic.kstest_normal(residuos) - ks_test_formatted = tuple(f"{val:.4f}" for val in ks_test) - - # Interpretando o resultado - estatistica_ks, p_valor_ks = ks_test # Desempacotando os valores - - if p_valor_ks > 0.05: - 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." - else: - ks_interpretacao = "Rejeitamos a hipótese nula. Há evidências estatísticas de que os resíduos não seguem uma distribuição normal." - - - # Distância de Cook - influencia = modelo.get_influence() - distancia_cook = influencia.cooks_distance[0] - - # Criar DataFrame com resultados por variável - coeficientes = modelo.params - erros_padrao = modelo.bse - t_values = modelo.tvalues - p_values = modelo.pvalues - - # Adicionar a coluna Escala com base no dicionário de escalas - escalas_coluna = [escalas[var][0] if var in escalas else "Nenhuma" for var in coeficientes.index] - - resultados_vars = pd.DataFrame({ - "Variável": coeficientes.index, - "Escala": escalas_coluna, # Adicionando a coluna Escala - "Coeficiente": coeficientes.values.round(4), - "Erro Padrão": erros_padrao.values.round(4), - "t-valor": t_values.values.round(4), - "P>|t|": p_values.values.round(4) - }) - - # Expressão da equação do modelo com a variável dependente transformada - if var_dep in escalas: - escala_y = escalas[var_dep][0] # Obter a escala associada - if escala_y == "ln(x)": - y_label_transformada = f"ln({var_dep})" - ajustar_termo = lambda termo: f"exp({termo})" # Aplicar exponencial - elif escala_y == "1/(x)": - y_label_transformada = f"1/({var_dep})" - ajustar_termo = lambda termo: f"1/({termo})" # Aplicar inverso - elif escala_y == "(x)²": - y_label_transformada = f"({var_dep})²" - ajustar_termo = lambda termo: f"sqrt({termo})" # Aplicar raiz quadrada - elif escala_y == "exp(x)": - y_label_transformada = f"exp({var_dep})" - ajustar_termo = lambda termo: f"ln({termo})" # Aplicar logaritmo natural - else: - y_label_transformada = var_dep # Sem transformação - ajustar_termo = lambda termo: termo # Sem ajuste - else: - y_label_transformada = var_dep # Sem transformação - ajustar_termo = lambda termo: termo # Sem ajuste - - # Construir os termos da equação - termos = [] - for var, coef in zip(coeficientes.index, coeficientes.values): - if var == 'const': - interseção = f"{coef:.4f}" - else: - if var in escalas: - escala_var = escalas[var][0] # Obter a escala associada - if escala_var == "ln(x)": - var_label = f"ln({var})" - elif escala_var == "1/(x)": - var_label = f"1/({var})" - elif escala_var == "(x)²": - var_label = f"({var})²" - elif escala_var == "exp(x)": - var_label = f"exp({var})" - else: - var_label = var # Sem transformação - else: - var_label = var # Sem transformação - termos.append(f"{coef:.4f} * {var_label}") - - # Montar a equação com a variável dependente transformada - lado_direito = interseção + " + " + " + ".join(termos) - equacao_transformada = f"{y_label_transformada} = {lado_direito}" - # Montar a equação com a variável dependente na escala direta - equacao_revertida = f"{var_dep} = {ajustar_termo(lado_direito)}" - # Substituir pontos por vírgulas nas equações - equacao_transformada = equacao_transformada.replace('.', ',') - equacao_revertida = equacao_revertida.replace('.', ',') - # Adicionar as duas formas da equação aos resultados gerais - #resultados_gerais += f"\nEquação do modelo (variável dependente transformada): {equacao_transformada}" - resultados_gerais += f"\nEquação do modelo (variável dependente na escala direta): {equacao_revertida}" - - # Classificar variáveis com base nos p-valores - def classificar(valor): - if valor > 0.3: - return "Fora dos critérios" - elif valor > 0.2: - return "Grau I" - elif valor > 0.1: - return "Grau II" - else: - return "Grau III" - - resultados_vars['Classificação'] = resultados_vars['P>|t|'].apply(classificar) - - # Determinar grau único considerando todas as variáveis - def determinar_grau_unico(classificacoes): - if "Fora dos critérios" in classificacoes: - return "Fora dos critérios" - elif "Grau I" in classificacoes: - return "Grau I" - elif "Grau II" in classificacoes: - return "Grau II" - else: - return "Grau III" - - tab5 = determinar_grau_unico(resultados_vars['Classificação']) - - # Enquadramento na NBR 14.653-2 - # Item 2 da tabela - if num_observacoes >= 6 * (num_variaveis + 1): - tab2 = "Grau III" - elif num_observacoes >= 4 * (num_variaveis + 1): - tab2 = "Grau II" - elif num_observacoes >= 3 * (num_variaveis + 1): - tab2 = "Grau I" - else: - tab2 = "Fora dos critérios" - - # Item 6 da tabela - if nivel_significancia <= 0.01: - tab6 = "Grau III" - elif nivel_significancia <= 0.02: - tab6 = "Grau II" - elif nivel_significancia <= 0.05: - tab6 = "Grau I" - else: - tab6 = "Fora dos critérios" - - # Resultados gerais formatados - resultados_gerais = f""" - Desvio Padrão dos Resíduos: {desvio_padrao_residuos} - Estatística F: {estatistica_F} | Nível de Significância: {nivel_significancia} - R²: {r_squared} | R² Ajustado: {r_squared_adjusted} - Correlação: {coef_correlacao} - Número de Observações: {num_observacoes} - Número de Variáveis Independentes: {num_variaveis} - - Fundamentação - Quant. min. dados (Item 2 tab 9.2.1 NBR 14.653-2): {tab2} - Fundamentação - Signif. Regressores (Item 5 tab 9.2.1 NBR 14.653-2): {tab5} - Fundamentação - Signif. Modelo (Item 6 tab 9.2.1 NBR 14.653-2): {tab6} - - Testes de normalidade: - 1) Comparação (curva normal) - Percentuais atingidos: {perc_resid} - Ideal 68% - aceitável de 64% a 75% - Ideal 90% - aceitável de 88% a 95% - Ideal 95% - aceitável de 95% a 100% - - Teste Kolmogorov-Smirnov: Estatística = {ks_test_formatted[0]}, Valor-p = {ks_test_formatted[1]} - ({ks_interpretacao}) - - Distância de Cook (Máxima): {np.max(distancia_cook):.8f} - - Equação do modelo: {equacao_revertida} - """ - - # Adicionando a coluna de erro padronizado ao df_final - df_original_res = df_original.copy() - df_original_res['Erro Padronizado'] = erro_padronizado - - # Criar DataFrame apenas com os dados cujo erro padronizado é maior que 2 - df_grandes_residuos = df_original_res[abs(df_original_res['Erro Padronizado']) > 2].copy() - df_grandes_residuos['Erro Abs'] = abs(df_grandes_residuos['Erro Padronizado']) - - # Listagem de pontos com resíduos > 2 - listagem_grandes_residuos = ", ".join(map(str, df_grandes_residuos.iloc[:, 0].tolist())) - - # Listagem dos pontos influenciantes - limite_cook = 1 - pontos_influentes = [] - for i, cook_dist in enumerate(distancia_cook): - if cook_dist > limite_cook: - pontos_influentes.append(dados_transformados.iloc[i]["Dado"]) # Usando a primeira coluna como rótulo - - # Transformando a lista em uma string separada por vírgula - listagem_pontos_influentes = ", ".join(map(str, pontos_influentes)) - - # Criação de um dataframe para valores previstos - valores_previstos = modelo.predict(X) - valores_previstos_trans = valores_previstos.copy() - # Reverter a escala da variável dependente, se aplicável - if var_dep in escalas: - escala_var_dep = escalas[var_dep][0] # Obtém a escala associada - - if escala_var_dep == "ln(x)": - valores_previstos = np.exp(valores_previstos) # Reverte ln(x) para x - elif escala_var_dep == "1/(x)": - valores_previstos = 1 / valores_previstos # Reverte 1/(x) para x - elif escala_var_dep == "(x)²": - valores_previstos = np.sqrt(valores_previstos) # Reverte (x)² para x - elif escala_var_dep == "exp(x)": - valores_previstos = np.log(valores_previstos) # Reverte exp(x) para x - # Caso não seja necessário reverter, mantém os valores ajustados como estão - - # Adicionando os valores ajustados como uma nova coluna ao DataFrame original - df_calc_obs = df_original.copy() - df_calc_obs['Valores Ajustados'] = round(valores_previstos, 8) - # Resíduo - df_calc_obs['Resíduo'] = (df_calc_obs[var_dep].replace(0, np.nan) - df_calc_obs['Valores Ajustados']).round(4) - # Erro padronizado - df_calc_obs['Erro Padronizado'] = erro_padronizado.round(4) - # Adicionando a coluna de Erro - # Certifique-se de evitar divisão por zero - df_calc_obs['Erro'] = df_calc_obs['Valores Ajustados'] / df_calc_obs[var_dep].replace(0, np.nan) - # Arredondar os valores da coluna 'Erro' para melhorar a apresentação - df_calc_obs['Erro'] = df_calc_obs['Erro'].round(4) - # Criando a coluna de erro percentual - df_calc_obs['Erro Percentual (%)'] = (abs(df_calc_obs['Erro'] - 1) * 100).round(4) - - # Adicionando os valores ajustados como uma nova coluna ao DataFrame original - df_calc_obs_trans = dados_transformados.copy() - df_calc_obs_trans['Valores Ajustados'] = round(valores_previstos_trans, 8) - - return resultados_gerais, resultados_vars, df_grandes_residuos, listagem_grandes_residuos, listagem_pontos_influentes, df_calc_obs, df_calc_obs_trans, erro_padronizado, modelo - else: - return "Erro: A variável dependente não está nos dados transformados.", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None - except Exception as e: - return f"Erro na regressão: {str(e)}", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None - else: - return "Erro: Dados transformados inválidos ou vazios.", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None - -# Função para plotar gráficos do modelo -def graficos(escala_dependente, df_calc_obs, df_calc_obs_trans, erro_padronizado, var_dep, num_bins=20): - - # Gráfico 1: Resíduos Padronizados - # Normalizar os resíduos padronizados para o colormap - residuos_norm = (np.abs(erro_padronizado) - np.abs(erro_padronizado).min()) / \ - (np.abs(erro_padronizado).max() - np.abs(erro_padronizado).min()) - - fig1 = go.Figure() - fig1.add_trace(go.Scatter( - x=df_calc_obs_trans['Valores Ajustados'], - y=erro_padronizado, - mode='markers', - marker=dict( - size=8, - color=residuos_norm, # Aplicar o colorscale - colorscale='Spectral', # Escolher a paleta de cores - ), - text=df_calc_obs_trans.iloc[:, 0], - hovertemplate="Índice: %{text}
Valores Ajustados: %{x:.2f}
Resíduos: %{y:.2f}" - )) - fig1.add_hline(y=0, line_dash="dash", line_color="black") - fig1.add_hline(y=2, line_dash="dot", line_color="red") - fig1.add_hline(y=-2, line_dash="dot", line_color="red") - fig1.update_layout( - title="Gráfico de Resíduos Padronizados", - xaxis_title="Valores Ajustados", - yaxis_title="Resíduos Padronizados", - template="plotly_white" - ) - - # Gráfico 2: Histograma dos Resíduos Padronizados com Curva Normal - # Calcula média e desvio padrão dos resíduos - mean_residuos = np.mean(erro_padronizado) - std_residuos = np.std(erro_padronizado) - - # Dados para a curva normal - x_vals = np.linspace(mean_residuos - 4 * std_residuos, mean_residuos + 4 * std_residuos, 500) - y_vals = stats.norm.pdf(x_vals, mean_residuos, std_residuos) # PDF da curva normal - - # Criar o histograma (frequência normalizada) - hist_values, bin_edges = np.histogram(erro_padronizado, bins=num_bins, density=True) - scale_factor = max(hist_values) / max(y_vals) # Ajustar altura da curva normal - y_vals_scaled = y_vals * scale_factor - - # Calcular o valor médio dos resíduos absolutos para cada bin - bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2 - bin_colors = 1 - (np.abs(bin_centers) - np.abs(bin_centers).min()) / (np.abs(bin_centers).max() - np.abs(bin_centers).min()) - - fig2 = go.Figure() - fig2.add_trace(go.Bar( - x=bin_centers, # Centraliza as barras - y=hist_values, - width=np.diff(bin_edges), - marker=dict( - color=bin_colors, # Aplicar o colorscale - colorscale='Reds', - ), - opacity=0.7, - name='Histograma' - )) - - # Adicionar a curva normal ajustada - fig2.add_trace(go.Scatter( - x=x_vals, - y=y_vals_scaled, - mode='lines', - line=dict(color='red', width=2), - name='Curva Normal' - )) - - fig2.update_layout( - title="Histograma dos Resíduos Padronizados com Curva Normal", - xaxis_title="Resíduos Padronizados", - yaxis_title="Frequência Normalizada", - template="plotly_white", - ) - - # Gráfico 3: Valores Ajustados vs Preços Observados - if escala_dependente == "Direta": - df_graf_ao = df_calc_obs - else: - df_graf_ao = df_calc_obs_trans - - # Definir os eixos - valores_observados = df_graf_ao.iloc[:, 1] # Segunda coluna do DataFrame - valores_calculados = df_graf_ao['Valores Ajustados'] - - # Cálculo dos resíduos normalizados - residuos = np.abs(valores_observados - valores_calculados) - residuos_norm = (residuos - residuos.min()) / (residuos.max() - residuos.min()) # Normalizar para o colorscale - - # Ajustar a reta de regressão linear com statsmodels - X = sm.add_constant(valores_observados) # Adicionar uma constante (intercepto) - modelo = sm.OLS(valores_calculados, X).fit() # Ajustar o modelo OLS - x_reta = np.linspace(valores_observados.min(), valores_observados.max(), 100) # Valores de X para a reta - y_reta = modelo.predict(sm.add_constant(x_reta)) # Predizer Y com base no modelo - - # Criar o gráfico - fig3 = go.Figure() - fig3.add_trace(go.Scatter( - x=valores_observados, - y=valores_calculados, - mode='markers', - marker=dict( - size=8, - color=residuos_norm, # Aplicar o colorscale - colorscale='Spectral', # Escolher a paleta de cores - showscale=False - ), - text=df_graf_ao.iloc[:, 0], # Índice ou outra coluna para hover - hovertemplate="Índice: %{text}
Observado: %{x:.2f}
Ajustado: %{y:.2f}", - showlegend=False # Remover legenda deste trace - )) - - # Adicionar a reta ajustada (linha de regressão) - fig3.add_trace(go.Scatter( - x=x_reta, - y=y_reta, - mode="lines", - line=dict(color="green", dash="solid"), - name="Reta Ajustada" - )) - - # Configuração do layout - fig3.update_layout( - title="Valores Ajustados vs Preços Observados", - xaxis_title="Preços Observados", - yaxis_title="Valores Ajustados", - template="plotly_white", - showlegend=False # Remover legenda - ) - - - - # Gráfico 4: Matriz de Correlações - corr_matrix = df_calc_obs_trans.drop(columns=["Dado", "Valores Ajustados"], errors='ignore').corr() - - # Criar o Heatmap diretamente com texto - fig4 = go.Figure() - - # Adicionar os valores manualmente como anotação no gráfico - for i, row in enumerate(corr_matrix.index): - for j, col in enumerate(corr_matrix.columns): - # Determinar a cor do texto com base nas condições - if row == col: - color = "black" # Preto para a diagonal (mesma variável) - elif corr_matrix.loc[row, col] > 0.8: # Correlação acima de 0.8 - if row == var_dep or col == var_dep: - color = "blue" #tr Azul para correlação com var_dep - else: - color = "red" # Vermelho para correlação alta entre variáveis independentes - else: - color = "black" # Preto para todas as outras correlações - - # Adicionar o texto no gráfico - fig4.add_trace(go.Scatter( - x=[col], - y=[row], - text=[f"{corr_matrix.loc[row, col]:.2f}"], - mode="text", - textfont=dict(size=12, color=color), # Aplicar a cor - )) - - # Atualizar o layout - fig4.update_layout( - title="Matriz de Correlações", - xaxis=dict(title="Variáveis", tickmode='array', tickvals=corr_matrix.columns), - yaxis=dict(title="Variáveis", tickmode='array', tickvals=corr_matrix.index), - template="plotly_white", - showlegend=False - ) - - return fig1, fig2, fig3, fig4 - -# Função para exportar para o excel -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): - try: - # Criar um arquivo Excel com múltiplas abas - with pd.ExcelWriter(nome_arquivo, engine='xlsxwriter') as writer: - # Adicionar os DataFrames em abas específicas - df_planilha.to_excel(writer, sheet_name='Dados Originais', index=False) - df_infos.to_excel(writer, sheet_name='Infos', index=False) - df_original.to_excel(writer, sheet_name='Dados Modelo', index=False) - #df_escalado.to_excel(writer, sheet_name='Dados Modelo Transformados', index=False) - df_outliers.to_excel(writer, sheet_name='Outliers', index=False) - - # Converter "Resultados Gerais" para um DataFrame formatado - resultados_lista = [linha.strip() for linha in resultados_gerais.split("\n") if linha.strip()] - resultados_df = pd.DataFrame({"Descrição": resultados_lista}) - resultados_df.to_excel(writer, sheet_name='Resultados Gerais', index=False) - - # Resultados por variável - resultados_vars.to_excel(writer, sheet_name='Resultados Variáveis', index=False) - - # Valores calculados x observados - df_calc_obs.to_excel(writer, sheet_name='Calc x Obs', index=False) - - # Valores calculados transformados - #df_calc_obs_trans.to_excel(writer, sheet_name='Calculados Transformados', index=False) - - return f"Arquivo '{nome_arquivo}' criado com sucesso." - except Exception as e: - return f"Erro ao criar o arquivo Excel: {str(e)}" - -# Função para salvar o modelo -def exportar_modelo_completo_avse(nome_pacote, modelo, resultados_gerais, df_planilha, df_infos, df_original, df_outliers, resultados_vars, df_calc_obs): - try: - # Verificar se o nome do pacote está vazio - if not nome_pacote: - return "Erro: O nome do arquivo não pode estar vazio." - - # Garantir que o nome do pacote tenha a extensão .avse - if not nome_pacote.endswith(".avse"): - nome_pacote += ".avse" - - # Empacotar todos os itens em um dicionário - pacote = { - "modelo": modelo, - "Resultados Gerais": resultados_gerais, - "df_planilha": df_planilha, - "df_infos": df_infos, - "df_original": df_original, - "df_outliers": df_outliers, - "resultados_vars": resultados_vars, - "df_calc_obs": df_calc_obs - } - - # Salvar o pacote usando joblib - dump(pacote, nome_pacote) - return f"Pacote '{nome_pacote}' criado com sucesso." - except Exception as e: - return f"Erro ao criar o pacote: {str(e)}" - -# Função para carregar o modelo -def carregar_modelo(nome_pacote): - try: - # Carregar o pacote salvo - pacote = joblib.load(nome_pacote) - - # Recuperar os objetos do pacote - modelo = pacote.get("modelo", None) # Objeto do modelo salvo - resultados_gerais = pacote.get("Resultados Gerais", "") # Resultados gerais do modelo - df_planilha = pacote.get("df_planilha", pd.DataFrame()) - df_infos = pacote.get("df_infos", pd.DataFrame()) - df_original = pacote.get("df_original", pd.DataFrame()) - df_outliers = pacote.get("df_outliers", pd.DataFrame()) - resultados_vars = pacote.get("resultados_vars", pd.DataFrame()) - df_calc_obs = pacote.get("df_calc_obs", pd.DataFrame()) - - # Adicionar uma linha em branco no df_infos se ele não estiver vazio - if not df_infos.empty: - df_infos.loc[len(df_infos)] = [None] * len(df_infos.columns) - - # Transpor o DataFrame - df_infos_transposed = df_infos.T - - # Renomear as colunas - df_infos_transposed.columns = ['Escalas', 'Máximo', 'Mínimo', 'Médio', 'Avaliando'] - - # Adicionar a coluna 'Variáveis' com os nomes das colunas originais - df_infos_transposed.insert(0, 'Variáveis', df_infos.columns) - - # Atribuir o valor padrão "------" à célula da primeira linha da última coluna - df_infos_transposed.iloc[0, -1] = "Variável dependente" - - # Obter apenas o nome do arquivo, sem o caminho - nome_arquivo = os.path.basename(nome_pacote) - - # Criar mensagem de sucesso com o nome do modelo - mensagem_sucesso = f"Modelo '{nome_arquivo}' carregado com sucesso." - - # Retornar todos os componentes necessários - return ( - mensagem_sucesso, # Mensagem de sucesso com o nome do modelo - modelo, # Modelo carregado para previsões - resultados_gerais, # Resultados gerais do modelo - df_planilha, # Dados originais - df_original, # Dados usados no modelo - df_outliers, # Outliers identificados - resultados_vars, # Resultados por variável - df_calc_obs, # Dados calculados x observados - df_infos_transposed, # Informações adicionais do modelo - ) - except Exception as e: - # Retornar valores padrão em caso de erro - return ( - "Carregue o modelo antes de clicar no botão VISUALIZAR MODELO", - None, # Modelo não carregado - "", # Resultados gerais vazio - pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), - ) - - - -def realizar_previsoes(modelo, tab_infos_carregada): - try: - if modelo is None or tab_infos_carregada.empty: - return "Erro: Modelo não carregado ou tabela de informações vazia.", pd.DataFrame(), pd.DataFrame(), "" - - # Identificar as escalas das variáveis independentes e dependente - escalas_independentes = tab_infos_carregada.iloc[1:, 1].to_list() # a partir da 2ª linha da 2ª coluna - escala_dependente = tab_infos_carregada.iloc[0, 1] # 1ª linha da 2ª coluna - - # Valores para previsão estão a partir da 2ª linha da 6ª coluna - valores_entrada = tab_infos_carregada.iloc[1:, 5].values - - # Converter valores para numéricos, substituindo inválidos por NaN - valores_entrada = pd.to_numeric(valores_entrada, errors='coerce') - - # Aplicar escalas às variáveis independentes - valores_transformados = [] - for valor, escala in zip(valores_entrada, escalas_independentes): - if np.isnan(valor): - valores_transformados.append(np.nan) # Manter NaN se o valor for inválido - elif escala == "ln(x)": - valores_transformados.append(np.log(valor) if valor > 0 else np.nan) # Evitar log de zero ou negativo - elif escala == "1/(x)": - valores_transformados.append(1 / valor if valor != 0 else np.nan) # Evitar divisão por zero - elif escala == "(x)²": - valores_transformados.append(valor ** 2) - elif escala == "exp(x)": - valores_transformados.append(np.exp(valor)) - else: - valores_transformados.append(valor) - - # Transformar em DataFrame para manter o formato necessário - df_previsao = pd.DataFrame([valores_transformados], columns=tab_infos_carregada.iloc[1:, 0]) - - # Adicionar constante ao modelo para previsão - X = sm.add_constant(df_previsao, has_constant='add') - - # Garantir que as colunas em X correspondam às usadas no modelo - colunas_modelo = modelo.model.exog_names - X = X.reindex(columns=colunas_modelo, fill_value=0) # Reordenar e preencher colunas faltantes com 0 - - # Verificar se existem valores NaN nos dados transformados - if X.isnull().values.any(): - return "Erro: Existem valores inválidos após a transformação. Verifique os dados de entrada.", df_previsao, pd.DataFrame(), "" - - # Realizar a previsão - previsao_transformada = modelo.predict(X).iloc[0] - - # Ajustar para a escala direta da variável dependente - if escala_dependente == "ln(x)": - previsao_final = np.exp(previsao_transformada) - elif escala_dependente == "1/(x)": - previsao_final = 1 / previsao_transformada - elif escala_dependente == "(x)²": - previsao_final = np.sqrt(previsao_transformada) - elif escala_dependente == "exp(x)": - previsao_final = np.log(previsao_transformada) - else: - previsao_final = previsao_transformada - - # Calcular limites do campo de arbítrio - limite_inferior_arbitrio = previsao_final * 0.85 - limite_superior_arbitrio = previsao_final * 1.15 - - # Calcular intervalo de confiança de 80% - intervalos = modelo.get_prediction(X).conf_int(alpha=0.2) - limite_inferior_ic = intervalos[0, 0] # Acessar diretamente o valor do array - limite_superior_ic = intervalos[0, 1] # Acessar diretamente o valor do array - - # Ajustar limites de IC para a escala direta, se necessário - if escala_dependente == "ln(x)": - limite_inferior_ic = np.exp(limite_inferior_ic) - limite_superior_ic = np.exp(limite_superior_ic) - elif escala_dependente == "1/(x)": - limite_inferior_ic = 1 / limite_inferior_ic - limite_superior_ic = 1 / limite_superior_ic - elif escala_dependente == "(x)²": - limite_inferior_ic = np.sqrt(limite_inferior_ic) - limite_superior_ic = np.sqrt(limite_superior_ic) - elif escala_dependente == "exp(x)": - limite_inferior_ic = np.log(limite_inferior_ic) - limite_superior_ic = np.log(limite_superior_ic) - - # Cálculo da extrapolação - extrapolacao_info = "" - valores_ajustados = valores_entrada.copy() - contagem_extrapolacoes = 0 - fora_dos_criterios = False - for i, valor in enumerate(valores_entrada): - maximo = pd.to_numeric(tab_infos_carregada.iloc[i+1, 2], errors='coerce') # Coluna 3 (Mínimo) - minimo = pd.to_numeric(tab_infos_carregada.iloc[i+1, 3], errors='coerce') # Coluna 4 (Máximo) - - if not np.isnan(valor): - if valor < minimo: - percentual_extrapolacao = ((minimo - valor) / minimo) * -100 - extrapolacao_info += f"Variável '{tab_infos_carregada.iloc[i+1, 0]}' está {percentual_extrapolacao:.2f}% abaixo do mínimo da amostra.\n" - valores_ajustados[i] = minimo # Ajustar valor para o mínimo - contagem_extrapolacoes += 1 - if percentual_extrapolacao < -50: - fora_dos_criterios = True - elif valor > maximo: - percentual_extrapolacao = ((valor - maximo) / maximo) * 100 - extrapolacao_info += f"Variável '{tab_infos_carregada.iloc[i+1, 0]}' está {percentual_extrapolacao:.2f}% acima do máximo da amostra.\n" - valores_ajustados[i] = maximo # Ajustar valor para o máximo - contagem_extrapolacoes += 1 - if percentual_extrapolacao > 100: - fora_dos_criterios = True - - # Criar novos DataFrames para cada variável que extrapolou - previsoes_extrapoladas = [] - for i, (valor, ajustado) in enumerate(zip(valores_entrada, valores_ajustados)): - if valor != ajustado: - valores_transformados_ext = [] - for valor_ext, escala in zip(valores_ajustados, escalas_independentes): - if np.isnan(valor_ext): - valores_transformados_ext.append(np.nan) - elif escala == "ln(x)": - valores_transformados_ext.append(np.log(valor_ext) if valor_ext > 0 else np.nan) - elif escala == "1/(x)": - valores_transformados_ext.append(1 / valor_ext if valor_ext != 0 else np.nan) - elif escala == "(x)²": - valores_transformados_ext.append(valor_ext ** 2) - elif escala == "exp(x)": - valores_transformados_ext.append(np.exp(valor_ext)) - else: - valores_transformados_ext.append(valor_ext) - - df_previsao_ext = pd.DataFrame([valores_transformados_ext], columns=tab_infos_carregada.iloc[1:, 0]) - X_ext = sm.add_constant(df_previsao_ext, has_constant='add') - X_ext = X_ext.reindex(columns=colunas_modelo, fill_value=0) - - if X_ext.isnull().values.any(): - continue - - previsao_transformada_ext = modelo.predict(X_ext).iloc[0] - - if escala_dependente == "ln(x)": - previsao_final_ext = np.exp(previsao_transformada_ext) - elif escala_dependente == "1/(x)": - previsao_final_ext = 1 / previsao_transformada_ext - elif escala_dependente == "(x)²": - previsao_final_ext = np.sqrt(previsao_transformada_ext) - elif escala_dependente == "exp(x)": - previsao_final_ext = np.log(previsao_transformada_ext) - else: - previsao_final_ext = previsao_transformada_ext - - previsoes_extrapoladas.append((df_previsao_ext, previsao_final_ext)) - - # Determinar a fundamentação da extrapolação - if fora_dos_criterios: - fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Fora dos critérios" - elif contagem_extrapolacoes == 1: - fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Grau II" - elif contagem_extrapolacoes > 1: - fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Grau I" - else: - fundamentacao = "" - - # Retornar as informações - resultado_texto = ( - f"Valor estimado central: {locale.currency(previsao_final, grouping=True)}\n" - "------------------------------------\n" - f"Limite inferior do campo de arbítrio (- 15% do Valor estimado central): {locale.currency(limite_inferior_arbitrio, grouping=True)}\n" - f"Limite superior do campo de arbítrio (+ 15% do Valor estimado central): {locale.currency(limite_superior_arbitrio, grouping=True)}\n" - "------------------------------------\n" - f"Limite inferior do Intervalo de Confiança de 80%: {locale.currency(limite_inferior_ic, grouping=True)}\n" - f"Limite superior do Intervalo de Confiança de 80%: {locale.currency(limite_superior_ic, grouping=True)}\n" - "------------------------------------\n" - f"{extrapolacao_info.strip()}\n" - f"{fundamentacao}\n" - ) - - - resultado_df = df_previsao - resultado_df_ext = pd.DataFrame() - resultado_texto_ext = "" - - if previsoes_extrapoladas: - for df_ext, previsao_ext in previsoes_extrapoladas: - resultado_df_ext = df_ext - resultado_texto_ext = ( - f"Valor estimado central (na fronteira amostral): {locale.currency(previsao_ext, grouping=True)}\n" - "------------------------------------\n" - ) - - # Cálculo da variação percentual - variacao_percentual = abs((previsao_final - previsao_ext) / previsao_final) * 100 - - # Determinar a fundamentação com base na variação percentual - if variacao_percentual > 20: - fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Fora dos critérios" - elif variacao_percentual > 15: - fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Grau I" - else: - fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Grau II" - - resultado_texto_ext += f"{fundamentacao_ext}\n" - - return resultado_df, resultado_texto, resultado_df_ext, resultado_texto_ext - - except Exception as e: - return pd.DataFrame(), "Erro ao realizar a previsão: {str(e)}", pd.DataFrame(), "" - - -#--------------------------------------------INTERFACE-------------------------------------------# - -def rl_tab(): - # Criação da aba - with gr.Tab("Regressão Linear"): - planilha_input = gr.File(label="Carregar Planilha", file_types=[".xls", ".xlsx"], elem_classes=["small-file-upload"]) - - with gr.Group(): - - #---------ESTADOS DE ARMAZENAMENTO-------------# - - # Estado para armazenamentos - cabeçalhos_state = gr.State([]) - dados_state = gr.State(pd.DataFrame()) - escalas_state = gr.State({}) - erro_padronizado_state = gr.State([]) - # Estados para armazenar os gráficos Plotly gerados - grafico_residuos_state = gr.State(None) - grafico_histograma_state = gr.State(None) - grafico_ajustados_state = gr.State(None) - matriz_corr_state = gr.State(None) - # Estado para armazenar o modelo gerado - modelo_state = gr.State(None) - - #---------PLANILHA DE ENTRADA-------------# - - # Checkbox para controlar a visibilidade da planilha carregada - 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) - tabela_planilha = gr.Dataframe(visible=False, max_height=250, elem_classes=["small span"]) # Oculto por padrão - - #---------GRÁFICOS DE DISPERSÃO-------------# - - # Gráficos de dispersão - with gr.Row(equal_height=True): - coluna_y_dispersao = gr.Dropdown(label="Eixo Y (Dispersão)", choices=[], interactive=True, visible=False, scale=3) - transformacao_y = gr.Dropdown( - label="Transformação para Eixo Y", - choices=["Nenhuma", "1/x", "ln(x)", "x²", "exp(x)"], - value="Nenhuma", - interactive=True, - visible=False, - scale=2 - ) - coluna_x_dispersao = gr.Dropdown(label="Eixo X (Dispersão)", choices=[], interactive=True, visible=False, scale=3) - transformacao_x = gr.Dropdown( - label="Transformação para Eixo X", - choices=["Nenhuma", "1/x", "ln(x)", "x²", "exp(x)"], - value="Nenhuma", - interactive=True, - visible=False, - scale=2 - ) - # Adicionando os gráficos de dispersão e boxplot lado a lado com o mesmo botão - botao_dispersao_boxplot = gr.Button("Gerar Gráficos de Dispersão e Boxplot", visible=False) - with gr.Row(): - grafico_dispersao_saida = gr.Plot(label="Gráfico de Dispersão", visible=False, scale=3) - grafico_boxplot_saida = gr.Plot(label="Gráfico Boxplot", visible=False, scale=1) - - #---------ESCOLHA DAS VARIÁVEIS-------------# - - # Checkbox para controlar a visibilidade dos dropdowns - mostrar_dropdowns = gr.Checkbox(label="VARIÁVEIS E ESCALAS (Variável dependente e variáveis independentes)", value=False, elem_classes="checkbox-yellow") - # Dropdown multi-select para cabeçalhos - # Título do conjunto - with gr.Group(): - 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 - with gr.Row(): - colunas_x = gr.Dropdown(label="(Direta: y ou x)", multiselect=True, choices=[], value=[], interactive=True, visible=False) - colunas_ln_x = gr.Dropdown(label="Logarítmica: ln(y) ou ln(x)", multiselect=True, choices=[], interactive=True, visible=False) - colunas_exp_x = gr.Dropdown(label="Exponencial: exp(y) ou exp(x)", multiselect=True, choices=[], interactive=True, visible=False) - colunas_inv_x = gr.Dropdown(label="Inversa: 1/(y) ou 1/(x)", multiselect=True, choices=[], interactive=True, visible=False) - colunas_quad_x = gr.Dropdown(label="Quadrática: (x)² ou (y²) ", multiselect=True, choices=[], interactive=True, visible=False) - # Escolha da variável dependente - var_dep = gr.Dropdown(label="Variável Dependente", multiselect=False, choices=[], interactive=True, visible=False) - - #---------DATAFRAMES-------------# - - with gr.Row(equal_height=True): - # Botão para gerar DataFrames - botao_gerar_df = gr.Button("Gerar Planilhas", scale=1) - # Checkbox para controlar visibilidade dos DataFrames gerados - mostrar_dataframes = gr.Checkbox(label="MOSTRAR PLANILHAS (Escalas e limites, Variáveis transformadas e Outliers)", value=False, scale=3) - # DataFrames para visualização - 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"]) - tabela_original = gr.Dataframe(label="Planilha original com Variáveis Escolhidas", max_height=250, visible=False, elem_classes=["small span"]) - tabela_escalada = gr.Dataframe(label="Planilha com Variáveis Transformadas", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"]) - #matriz_correl = gr.Dataframe(label="Matriz de correlações", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"]) - df_out = gr.Dataframe(label="Outliers", max_height=150, visible=False, interactive=True, headers=None, elem_classes=["small span"]) - - #---------MODELO-------------# - - with gr.Row(equal_height=True): - # Adicionando botão para executar a regressão - botao_regressao = gr.Button("Gerar Modelo", scale=1) - # Checkbox para visualizar os resultados - 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) - # Resultados gerais e por variáveis - with gr.Row(): - resultado_geral = gr.Textbox(label="Resultados Gerais", lines=25, interactive=False, visible=False, scale=1) - resultado_coef = gr.Dataframe(label="Resultados por Variável", interactive=False, visible=False, scale=1, elem_classes=["small span"]) - # Resíduos (dataframe e listagem) - residuos = gr.Dataframe(label="Resíduos padronizados > 2", interactive=False, max_height=250, visible=False, scale=1, elem_classes=["small span"]) - with gr.Row(): - # Resíduos > 2 - residuos_list = gr.Textbox(label="Listagem resíduos padronizados > 2", lines=2, interactive=False, visible=False, scale=2) - # Pontos influenciantes - pontos_inf = gr.Textbox(label="Listagem dos Pontos Influenciantes", lines=2, interactive=False, visible=False, scale=1) - # Entrada dinâmica - 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", - interactive=True, visible=False, scale=2) - - # Adicionando a visualização para df_calc_obs - calc_obs = gr.Dataframe(label="Calculados x Observados", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"]) - calc_obs_trans = gr.Dataframe(label="Calculados Transformados", max_height=250, visible=False, interactive=True, elem_classes=["small span"]) - - #---------GRÁFICOS DO MODELO-------------# - - with gr.Row(equal_height=True): - # Adicionar botão para gerar gráficos - botao_graficos = gr.Button("Gerar Gráficos", scale=1) - # Checkbox para controlar a visibilidade da seção de gráficos do modelo - 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) - # Escolha a escala da variável dependente para o gráfico Ajustados x Observado - escala_graf_ao = gr.Dropdown(label="Escolha a escala da variável dependente para o gráfico Valores Ajustados x Observado", choices=["Transformada", "Direta"]) - - # Adicionar rádio para selecionar o gráfico - grafico_selecao = gr.Radio( - choices=["Resíduos Padronizados", "Histograma", "Valores Ajustados vs Observados", "Matriz de Correlações"], - label="Selecione o Gráfico", - interactive=True, - visible=False - ) - - # Adicionar saída para o gráfico selecionado - grafico_saida = gr.Plot(label="Gráfico Selecionado", visible=False) - - #---------EXPORTAR PARA O EXCEL-------------# - - with gr.Row(equal_height=True): - botao_exportar_excel = gr.Button("Exportar Planilha") - arquivo_excel = gr.File(label="Baixar Excel", interactive=False, visible=False) - - #---------SALVAR MODELO-------------# - - with gr.Row(equal_height=True): - # Adicionar campo de entrada para o nome do arquivo - nome_arquivo_modelo = gr.Textbox( - label="Nome do Arquivo do Modelo", - placeholder="Digite o nome do modelo e clique no botão ao lado para salvar", - interactive=True, - scale=3 - ) - # Adicionar botão "SALVAR MODELO" e output para indicar o sucesso/erro - botao_salvar_modelo = gr.Button("Salvar Modelo", scale=1) - saida_salvar_modelo = gr.Textbox( - label="Status do Salvamento do Modelo", - interactive=False, - visible=False, - lines=2 - ) - - #---------CARREGAR MODELO-------------# - - with gr.Row(equal_height=True): - # Entrada para carregar o modelo - modelo_input = gr.File(label="Carregar Modelo", file_types=[".avse"], elem_classes=["small-file-upload"]) - # Botão para carregar o modelo - botao_carregar_modelo = gr.Button("Visualizar Modelo") - # Checkbox abaixo do botão "Visualizar Modelo" - mostrar_modelo_checkbox = gr.Checkbox( - label="MOSTRAR DETALHES DO MODELO", - value=False, - elem_classes=["checkbox-yellow"] - ) - # Mensagem de status para aparecer logo abaixo do modelo_input - status_carregamento = gr.Textbox( - label="Status do Carregamento do Modelo", - interactive=False, - visible=False, # Certifique-se de que está visível - lines=1, - elem_classes=["small span"] - ) - - # TextBox para exibir os resultados gerais - resultado_geral_carregado = gr.Textbox( - label="Resultados Gerais (Carregados)", - interactive=False, - visible=False, - lines=25, # Ajuste o número de linhas conforme necessário - elem_classes=["small span"] - ) - - # DataFrames para exibição - tabela_planilha_carregada = gr.Dataframe(label="Dados Originais", visible=False, max_height=250, elem_classes=["small span"]) - tabela_original_carregada = gr.Dataframe(label="Dados Modelo", visible=False, max_height=250, elem_classes=["small span"]) - tabela_outliers_carregada = gr.Dataframe(label="Outliers", visible=False, max_height=250, elem_classes=["small span"]) - tabela_resultados_vars_carregada = gr.Dataframe(label="Resultados Variáveis", visible=False, max_height=250, elem_classes=["small span"]) - tabela_calc_obs_carregada = gr.Dataframe(label="Calculados x Observados", visible=False, max_height=250, elem_classes=["small span"]) - tab_infos_carregada = gr.Dataframe(label="Variáveis, escalas, limites e avaliando", interactive=True, visible=False, elem_classes=["small span"]) - - #---------AVALIAÇÃO-------------# - - botao_previsao = gr.Button("Realizar Previsão") - tabela_previsao = gr.Dataframe(label="Valores Transformados para Previsão", visible=True, max_height=250) - previsao_saida = gr.Textbox(label="Resultado da Previsão", lines=8, interactive=False, visible=True) - - # Adicionar DataFrames para exibir os resultados das previsões ajustadas - tabela_previsao_ajustada = gr.Dataframe(label="Valores Transformados para Previsão Ajustada", visible=True, max_height=250) - previsao_ajustada_saida = gr.Textbox(label="Resultado da Previsão Ajustada", lines=8, interactive=False, visible=True) - - # 1. CONTROLE DE VISIBILIDADE - - # 1.1. Mostrar ou ocultar a planilha carregada e os gráficos - mostrar_planilha.change( - lambda visible, dados: [gr.update(visible=visible, value=dados)] * 8, - inputs=[mostrar_planilha, dados_state], - outputs=[ - tabela_planilha, coluna_y_dispersao, transformacao_y, coluna_x_dispersao, - transformacao_x, botao_dispersao_boxplot, - grafico_dispersao_saida, grafico_boxplot_saida - ] - ) - - # 1.2. Mostrar ou ocultar os dropdowns para escolha de variáveis transformadas - mostrar_dropdowns.change( - lambda visible: [gr.update(visible=visible)] * 7, - inputs=[mostrar_dropdowns], - outputs=[var_ind, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, var_dep] - ) - - # 1.3. Mostrar ou ocultar os DataFrames gerados (cabeçalhos, escalas, correlação, etc.) - mostrar_dataframes.change( - lambda visible: [gr.update(visible=visible)] * 3, - inputs=[mostrar_dataframes], - outputs=[tabela_cabecalhos, tabela_escalada, df_out] #matriz_correl, - ) - - # 1.4. Mostrar ou ocultar os resultados da regressão (resultados gerais, resíduos, etc.) - mostrar_resultados.change( - lambda visible: [gr.update(visible=visible)] * 7, - inputs=[mostrar_resultados], - outputs=[resultado_geral, resultado_coef, residuos, residuos_list, pontos_inf, entrada_dinamica, calc_obs] - ) - - # 1.5. Mostrar ou ocultar a seção de gráficos e componentes associados - mostrar_graficos.change( - lambda visible: [gr.update(visible=visible)] * 2, - inputs=[mostrar_graficos], - outputs=[grafico_selecao, grafico_saida] - ) - - # 1.6. Mostrar ou ocultar os detalhes do modelo carregado - mostrar_modelo_checkbox.change( - lambda visible: [gr.update(visible=visible)] * 8, - inputs=[mostrar_modelo_checkbox], - outputs=[ - resultado_geral_carregado, tabela_planilha_carregada, tabela_original_carregada, - tabela_outliers_carregada, tabela_resultados_vars_carregada, tabela_calc_obs_carregada, - tab_infos_carregada, status_carregamento - ] - ) - - # 2. CARREGAMENTO DE PLANILHA E ATUALIZAÇÃO DE DADOS - - # 2.1. Carregar a planilha e extrair cabeçalhos e dados - planilha_input.change( - lambda file: carregar_planilha(file), - inputs=[planilha_input], - outputs=[cabeçalhos_state, dados_state] - ) - - # 2.2. Atualizar as opções de dropdown para o gráfico de dispersão ao carregar uma planilha - cabeçalhos_state.change( - lambda cabecalhos: [gr.update(choices=cabecalhos, value=None)] * 2, - inputs=[cabeçalhos_state], - outputs=[coluna_y_dispersao, coluna_x_dispersao] - ) - - # 2.3. Atualizar os dropdowns para transformações e seleção de variáveis dependentes/independentes - cabeçalhos_state.change( - atualizar_dropdowns, - inputs=[cabeçalhos_state], - outputs=[colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, var_dep] - ) - - # 2.4. Atualizar o gráfico exibido com base na seleção do usuário - grafico_selecao.change( - lambda escala, selecao, fig1, fig2, fig3, fig4: ( - fig1 if selecao == "Resíduos Padronizados" else - (fig2 if selecao == "Histograma" else - (fig3 if selecao == "Valores Ajustados vs Observados" else fig4)) - ), - inputs=[escala_graf_ao, grafico_selecao, grafico_residuos_state, grafico_histograma_state, grafico_ajustados_state, matriz_corr_state], - outputs=[grafico_saida] - ) - - # 3. BOTÕES - # 3.1. Botão para conectar à função de Gerar os gráficos de dispersão e boxplot (GERAR DISPERSÃO E BOXPLOT) - botao_dispersao_boxplot.click( - lambda df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out: ( - grafico_dispersao(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out), - grafico_boxplot(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out) - ), - inputs=[dados_state, coluna_y_dispersao, transformacao_y, coluna_x_dispersao, transformacao_x, entrada_dinamica], - outputs=[grafico_dispersao_saida, grafico_boxplot_saida] - ) - - # 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 - # transformados, escalados e correlação (GERAR PLANILHAS) - botao_gerar_df.click( - criar_dataframe_cabecalhos, - inputs=[cabeçalhos_state, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, dados_state, entrada_dinamica, var_dep], - outputs=[tabela_cabecalhos, escalas_state] - ) - - botao_gerar_df.click( - criar_dataframes, - inputs=[dados_state, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, entrada_dinamica, var_dep], - outputs=[tabela_original, tabela_escalada, df_out] # , matriz_correl - ) - - # 3.3. Botão para conectar à função de realizar regressão linear com base nas variáveis selecionadas (GERAR MODELO) - botao_regressao.click( - realizar_regressao, - inputs=[var_dep, tabela_escalada, tabela_original, escalas_state], - outputs=[resultado_geral, resultado_coef, residuos, residuos_list, pontos_inf, calc_obs, calc_obs_trans, - erro_padronizado_state, modelo_state] - ) - - # 3.4. Botão para conectar à função de Gerar gráficos do modelo (GERAR GRÁFICOS - resíduos, histograma, ajustados vs observados) - botao_graficos.click( - graficos, # Função que gera os gráficos Plotly - inputs=[escala_graf_ao, calc_obs, calc_obs_trans, erro_padronizado_state, var_dep], - outputs=[grafico_residuos_state, grafico_histograma_state, grafico_ajustados_state, matriz_corr_state] - ) - - # 3.5. Botão para conectar à função de exportar para o excel (EXPORTAR PLANILHA) - botao_exportar_excel.click( - lambda df_planilha, df_infos, df_original, df_escalado, df_out, resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans: ( - "resultado.xlsx", - exportar_para_excel( - "resultado.xlsx", df_planilha, df_infos, df_original, df_escalado, df_out, - resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans - ) - )[0], - inputs=[ - dados_state, tabela_cabecalhos, tabela_original, tabela_escalada, df_out, - resultado_geral, resultado_coef, calc_obs, calc_obs_trans - ], - outputs=[arquivo_excel] - ) - # Atualizar visibilidade do status após clique no botão - botao_exportar_excel.click( - lambda *args: gr.update(visible=True), - inputs=[], - outputs=[arquivo_excel] - ) - - # 3.6. Botão para conectar à função de salvar o modelo (SALVAR MODELO) - botao_salvar_modelo.click( - lambda nome_arquivo, modelo, resultados_gerais, df_planilha, df_infos, df_original, df_outliers, resultados_vars, df_calc_obs: ( - exportar_modelo_completo_avse( - nome_arquivo, modelo, resultados_gerais, df_planilha, df_infos, df_original, - df_outliers, resultados_vars, df_calc_obs - ) - ), - inputs=[ - nome_arquivo_modelo, modelo_state, resultado_geral, dados_state, tabela_cabecalhos, - tabela_original, df_out, resultado_coef, calc_obs - ], - outputs=[saida_salvar_modelo] - ) - # Atualizar visibilidade do status após clique no botão - botao_salvar_modelo.click( - lambda: gr.update(visible=True), - inputs=[], - outputs=[saida_salvar_modelo] - ) - - # 3.7. Botão para conectar à função de carregamento do modelo (VISUALIZAR MODELO) - botao_carregar_modelo.click( - carregar_modelo, - inputs=[modelo_input], - outputs=[ - status_carregamento, # Mensagem de status aparece logo abaixo da caixa de upload - modelo_state, # Estado para armazenar o modelo carregado - resultado_geral_carregado, - tabela_planilha_carregada, tabela_original_carregada, tabela_outliers_carregada, - tabela_resultados_vars_carregada, tabela_calc_obs_carregada, tab_infos_carregada - ] - ) - - # 3.8. Botão para conectar à função de realizar previsões (REALIZAR PREVISÃO) - botao_previsao.click( - realizar_previsoes, - inputs=[modelo_state, tab_infos_carregada], - outputs=[tabela_previsao, previsao_saida, tabela_previsao_ajustada, previsao_ajustada_saida] - ) - - return locals() - - -### Pontos de máximo e mínimo -### Colunas com Coordenadas (ter a possibilidade de dizer se há ou não coordenadas para criar uma dispesão espacial) -### Endereços -### Colunas que possuem valor 0, não podem ser utilizados na ln ou inversa (mensagem de erro?) -### Micronumerosidade -### Média, mediana e moda -### Problema no ordenamento dos dataframes -### Implementar avaliação em massa -### Implementar relatório em word +import gradio as gr +import pandas as pd +import numpy as np +import statsmodels.api as sm +import matplotlib.pyplot as plt +import seaborn as sns +import plotly.graph_objects as go +import plotly.express as px +import scipy.stats as stats +from joblib import dump +import joblib +import os +import locale + +try: + locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8') +except locale.Error: + # Fallback para uma localidade padrão (ex.: 'C' ou 'en_US.UTF-8') + locale.setlocale(locale.LC_ALL, 'C') + +# Função para carregar a planilha de dados +def carregar_planilha(file): + try: + # Carregar o arquivo Excel sem forçar o tipo inicialmente + df = pd.read_excel(file.name) + + # Garantir que todas as colunas sejam convertidas para float, ignorando erros + for col in df.columns: + df[col] = pd.to_numeric(df[col], errors='coerce') # Converte para número, valores inválidos viram NaN + + # Substituir NaN por zero ou outro valor padrão, se necessário + df.fillna(0, inplace=True) + + # Garantir que os cabeçalhos sejam strings + df.columns = [str(col) for col in df.columns] + + # Adicionar índice baseado na posição + df.insert(0, "Dado", range(1, len(df) + 1)) + + # Arredondar colunas de tipo float para 4 casas decimais + for col in df.select_dtypes(include=[float]).columns: + df[col] = df[col].round(4) + + cabecalhos = list(df.columns) + return cabecalhos, df + except Exception as e: + print(f"Erro ao carregar a planilha: {e}") + return [], pd.DataFrame() + +# Função para aplicar a transformação selecionada +def aplicar_transformacao(df, coluna, transformacao): + try: + if transformacao == "1/x": + return 1 / df[coluna].replace(0, np.nan).fillna(0) # Evitar divisão por zero + elif transformacao == "ln(x)": + return np.log(df[coluna].replace(0, np.nan).fillna(0)) # Evitar log de zero + elif transformacao == "x²": + return df[coluna] ** 2 + elif transformacao == "exp(x)": + return np.exp(df[coluna]) + else: + return df[coluna] # Sem transformação, retorna a coluna original + except Exception as e: + return df[coluna] # Retorna original em caso de erro + +# Função para criar gráficos de dispersão para análise exploratória com transformações +def grafico_dispersao(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out): + if df.empty or x_coluna not in df.columns or y_coluna not in df.columns: + return None # Retornar None se o DataFrame estiver vazio ou as colunas não forem válidas + + # Copiar o DataFrame para manipulação + df_grafico = df.copy() + # Convertendo a entrada manual em uma lista de inteiros + dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] + # Removendo os outliers dos DataFrames + df_grafico = df_grafico[~df_grafico["Dado"].isin(dados_out)] + # Aplicar transformações nas colunas x e y + x = aplicar_transformacao(df_grafico, x_coluna, transformacao_x) + y = aplicar_transformacao(df_grafico, y_coluna, transformacao_y) + # Calcular a linha de tendência + coef = np.polyfit(x, y, 1) # Coeficientes da linha de tendência (linear) + linha_tendencia = np.poly1d(coef) + y_tendencia = linha_tendencia(x) + # Calcular resíduos (distância dos pontos à linha de tendência) + residuos = np.abs(y - y_tendencia) + # Normalizar os resíduos para aplicar o colormap + residuos_norm = (residuos - residuos.min()) / (residuos.max() - residuos.min()) + # Calcular o valor de correlação + correlacao = np.corrcoef(x, y)[0, 1] + # Criar o gráfico de dispersão com Plotly + fig = go.Figure() + # Adicionar os pontos ao gráfico com colormap + fig.add_trace(go.Scatter( + x=x, + y=y, + mode='markers', + marker=dict( + size=8, + color=residuos_norm, # Colormap com base nos resíduos + colorscale='Spectral', # Escolher o esquema de cores (pode ser ajustado) + showscale=False, # Desabilitar a barra de cores + ), + text=[f"Índice: {idx}
{x_coluna}: {x_val:.2f}
{y_coluna}: {y_val:.2f}
Resíduo: {resid:.2f}" + for idx, x_val, y_val, resid in zip(df_grafico["Dado"], x, y, residuos)], # Informações no tooltip + hoverinfo="text" + )) + + # Adicionar a linha de tendência ao gráfico + fig.add_trace(go.Scatter( + x=x, + y=y_tendencia, + mode='lines', + line=dict(color='darkred', width=2), # Linha de tendência com vermelho escuro + )) + + # Atualizar o layout do gráfico + fig.update_layout( + title=f"Gráfico de Dispersão: {x_coluna} vs {y_coluna}
Correlação: {correlacao:.2f}", + xaxis_title=f"{x_coluna} ({transformacao_x})", + yaxis_title=f"{y_coluna} ({transformacao_y})", + template="plotly_white", + showlegend=False + ) + + return fig + +# Função para criar boxplot para análise exploratória com transformações +def grafico_boxplot(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out): + if df.empty or y_coluna not in df.columns: + return None # Retorna None se o DataFrame estiver vazio ou as colunas não forem válidas + + # Copiar o DataFrame para manipulação + df_boxplot = df.copy() + + # Convertendo a entrada manual em uma lista de inteiros + dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] + # Removendo os outliers dos DataFrames + df_boxplot = df_boxplot[~df_boxplot["Dado"].isin(dados_out)] + + # Aplicar transformação na coluna Y + y = aplicar_transformacao(df_boxplot, y_coluna, transformacao_y) + + # Criar lista de textos personalizados para hover + hover_text = [ + f"Índice: {idx}
{y_coluna}: {y_val:.2f}" + for idx, y_val in zip(df_boxplot["Dado"], y) + ] + + # Criar o gráfico de boxplot com Plotly + fig = go.Figure() + + # Adicionar os dados ao gráfico + fig.add_trace(go.Box( + y=y, + name=y_coluna, + boxpoints='all', # Mostra todos os pontos, incluindo outliers + jitter=0.8, # Espalhamento horizontal dos pontos + pointpos=-1.8, # Posição relativa dos pontos em relação ao boxplot + marker=dict( + color='orange', + size=8 + ), + line=dict(color='gray'), + hovertemplate="%{text}", # Personaliza o hover + text=hover_text # Insere a lista de textos no hover + )) + + # Atualizar o layout do gráfico + fig.update_layout( + title=f"Boxplot da Coluna: {y_coluna} ({transformacao_y})", + yaxis_title=f"{y_coluna} ({transformacao_y})", + template="plotly_white", + showlegend=False + ) + + return fig + +# Função para atualizar os dropdowns +def atualizar_dropdowns(cabeçalhos): + if cabeçalhos: + # Remover 'Índice' das opções, se não for relevante para as análises + cabecalhos_sem_indice = [col for col in cabeçalhos if col != "Dado"] + dropdown_update_x = gr.update(choices=cabecalhos_sem_indice, value=cabecalhos_sem_indice) + dropdown_update = gr.update(choices=cabecalhos_sem_indice, value=[]) + return [dropdown_update_x] + [dropdown_update] * 4 + [dropdown_update] # Inclui var_dep + else: + dropdown_reset = gr.update(choices=[], value=[]) + return [dropdown_reset] * 5 + [dropdown_reset] # Inclui var_dep + +# Função para criar os cabeçalhos e colocar os limites por variável sem os outliers +def criar_dataframe_cabecalhos(cabecalhos, x, ln_x, exp_x, inv_x, quad_x, dados, dados_out, var_dep): + if dados.empty: + return pd.DataFrame(), {} + + # Remover os outliers antes de calcular os limites + if dados_out: + dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] + dados = dados[~dados["Dado"].isin(dados_out)] + + # Criar um dicionário com as seleções + escalas = { + col: [escala] for col, escala in zip( + x + ln_x + exp_x + inv_x + quad_x, + ["(x)"] * len(x) + ["ln(x)"] * len(ln_x) + ["exp(x)"] * len(exp_x) + + ["1/(x)"] * len(inv_x) + ["(x)²"] * len(quad_x) + ) + } + + # Criar o DataFrame base + df = pd.DataFrame.from_dict(escalas, orient='index', columns=['Escala']).transpose() + + # Reorganizar as colunas para garantir que 'var_dep' seja a primeira + if var_dep in df.columns: + cols = [var_dep] + [col for col in df.columns if col != var_dep] + df = df[cols] + + # Calcular as linhas adicionais (máximo, mínimo, média) com duas casas decimais + limites = { + "Máximo": dados.max().round(2), + "Mínimo": dados.min().round(2), + "Média": dados.mean().round(2) + } + + # Adicionar as linhas ao DataFrame + for label, valores in limites.items(): + linha = {col: valores[col] if col in valores else '' for col in df.columns} + df = pd.concat([df, pd.DataFrame([linha], index=[label])]) + + return df, escalas + +# Função para criar DataFrames original, escalado, correlação e outliers +def criar_dataframes(dados, x, ln_x, exp_x, inv_x, quad_x, dados_out, var_dep): + if dados.empty: + return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), None + + # Selecionar as colunas escolhidas e incluir "Dado" como identificador + colunas_escolhidas = x + ln_x + exp_x + inv_x + quad_x + df_original = dados[["Dado"] + colunas_escolhidas] # Incluindo "Dado" + + # Aplicar escalas e manter "Dado" + df_escalado = pd.DataFrame() + df_escalado["Dado"] = dados["Dado"] # Preservar "Dado" no escalado + + for col in x: + df_escalado[col] = dados[col] # (x) + for col in ln_x: + df_escalado[col] = np.log(dados[col].replace(0, np.nan)).fillna(0) # ln(x), evitando log de zero + for col in exp_x: + df_escalado[col] = np.exp(dados[col]) # exp(x) + for col in inv_x: + df_escalado[col] = 1 / dados[col].replace(0, np.nan).fillna(0) # 1/(x), evitando divisão por zero + for col in quad_x: + df_escalado[col] = dados[col] ** 2 # (x)² + + # Reorganizar colunas para que var_dep fique como a segunda coluna + if var_dep in df_original.columns: + colunas_reorganizadas_original = ( + ["Dado", var_dep] + [col for col in df_original.columns if col not in ["Dado", var_dep]] + ) + df_original = df_original[colunas_reorganizadas_original] + + if var_dep in df_escalado.columns: + colunas_reorganizadas_escalado = ( + ["Dado", var_dep] + [col for col in df_escalado.columns if col not in ["Dado", var_dep]] + ) + df_escalado = df_escalado[colunas_reorganizadas_escalado] + + # Criando o novo DataFrame com a coluna 'variáveis' sem índice + df_variaveis = pd.DataFrame({'variáveis': df_original.columns}) + df_variaveis.reset_index(drop=True, inplace=True) + + # Verificar se dados_out está em branco + if not dados_out.strip(): + df_outliers = pd.DataFrame() # DataFrame vazio para outliers + else: + # Convertendo a entrada manual em uma lista de inteiros + dados_out = [int(num.strip()) for num in dados_out.split(",") if num.strip()] + # Criando o DataFrame de outliers + df_outliers = df_original[df_original["Dado"].isin(dados_out)] # Filtrar com base nos valores de dados_out + # Removendo os outliers dos DataFrames originais e escalados + df_original = df_original[~df_original["Dado"].isin(dados_out)] + df_escalado = df_escalado[~df_escalado["Dado"].isin(dados_out)] + + # Criar matriz de correlação + correlation_matrix = df_escalado.drop(columns=["Dado"]).corr().round(2) # "Dado" não entra na correlação + + return df_original, df_escalado, df_outliers # , correlation_matrix + +# Função para Regressão Linear +def realizar_regressao(var_dep, dados_transformados, df_original, escalas): + if isinstance(dados_transformados, pd.DataFrame) and not dados_transformados.empty: + try: + import statsmodels.api as sm + import numpy as np + + if var_dep in dados_transformados.columns: + # Separando X e y + y = dados_transformados[var_dep] + X = dados_transformados.drop(columns=[var_dep, "Dado"]) + + # Número de variáveis independentes + num_variaveis = X.shape[1] + + # Adicionando constante + X = sm.add_constant(X) + + # Ajustando modelo + modelo = sm.OLS(y, X).fit() + + # Inicializar resultados gerais + resultados_gerais = "" + + # Estatísticas gerais do modelo + residuos = modelo.resid + desvio_padrao_residuos = round(np.std(residuos), 8) + erro_padronizado = np.round(residuos / desvio_padrao_residuos, 8) + estatistica_F = round(modelo.fvalue, 8) + nivel_significancia = round(modelo.f_pvalue, 8) + r_squared = round(modelo.rsquared, 8) + r_squared_adjusted = round(modelo.rsquared_adj, 8) + num_observacoes = int(round(modelo.nobs, 0)) + coef_correlacao = round(np.sqrt(r_squared), 8) + + # Comparação com a curva normal de resíduos + intervalos = [(-1.00, 1.00), (-1.64, 1.64), (-1.96, 1.96)] + percentuais = [] + for intervalo in intervalos: + min_intervalo, max_intervalo = intervalo + count = np.sum((erro_padronizado >= min_intervalo) & (erro_padronizado <= max_intervalo)) + percentual = round(count / len(erro_padronizado) * 100, 0) + percentuais.append(f"{percentual:.0f}%") + perc_resid = ", ".join(percentuais) + + # Teste Kolmogorov-Smirnov (KS) + ks_test = sm.stats.diagnostic.kstest_normal(residuos) + ks_test_formatted = tuple(f"{val:.4f}" for val in ks_test) + + import statsmodels.api as sm + + # Teste Kolmogorov-Smirnov (KS) + ks_test = sm.stats.diagnostic.kstest_normal(residuos) + ks_test_formatted = tuple(f"{val:.4f}" for val in ks_test) + + # Interpretando o resultado + estatistica_ks, p_valor_ks = ks_test # Desempacotando os valores + + if p_valor_ks > 0.05: + 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." + else: + ks_interpretacao = "Rejeitamos a hipótese nula. Há evidências estatísticas de que os resíduos não seguem uma distribuição normal." + + + # Distância de Cook + influencia = modelo.get_influence() + distancia_cook = influencia.cooks_distance[0] + + # Criar DataFrame com resultados por variável + coeficientes = modelo.params + erros_padrao = modelo.bse + t_values = modelo.tvalues + p_values = modelo.pvalues + + # Adicionar a coluna Escala com base no dicionário de escalas + escalas_coluna = [escalas[var][0] if var in escalas else "Nenhuma" for var in coeficientes.index] + + resultados_vars = pd.DataFrame({ + "Variável": coeficientes.index, + "Escala": escalas_coluna, # Adicionando a coluna Escala + "Coeficiente": coeficientes.values.round(4), + "Erro Padrão": erros_padrao.values.round(4), + "t-valor": t_values.values.round(4), + "P>|t|": p_values.values.round(4) + }) + + # Expressão da equação do modelo com a variável dependente transformada + if var_dep in escalas: + escala_y = escalas[var_dep][0] # Obter a escala associada + if escala_y == "ln(x)": + y_label_transformada = f"ln({var_dep})" + ajustar_termo = lambda termo: f"exp({termo})" # Aplicar exponencial + elif escala_y == "1/(x)": + y_label_transformada = f"1/({var_dep})" + ajustar_termo = lambda termo: f"1/({termo})" # Aplicar inverso + elif escala_y == "(x)²": + y_label_transformada = f"({var_dep})²" + ajustar_termo = lambda termo: f"sqrt({termo})" # Aplicar raiz quadrada + elif escala_y == "exp(x)": + y_label_transformada = f"exp({var_dep})" + ajustar_termo = lambda termo: f"ln({termo})" # Aplicar logaritmo natural + else: + y_label_transformada = var_dep # Sem transformação + ajustar_termo = lambda termo: termo # Sem ajuste + else: + y_label_transformada = var_dep # Sem transformação + ajustar_termo = lambda termo: termo # Sem ajuste + + # Construir os termos da equação + termos = [] + for var, coef in zip(coeficientes.index, coeficientes.values): + if var == 'const': + interseção = f"{coef:.4f}" + else: + if var in escalas: + escala_var = escalas[var][0] # Obter a escala associada + if escala_var == "ln(x)": + var_label = f"ln({var})" + elif escala_var == "1/(x)": + var_label = f"1/({var})" + elif escala_var == "(x)²": + var_label = f"({var})²" + elif escala_var == "exp(x)": + var_label = f"exp({var})" + else: + var_label = var # Sem transformação + else: + var_label = var # Sem transformação + termos.append(f"{coef:.4f} * {var_label}") + + # Montar a equação com a variável dependente transformada + lado_direito = interseção + " + " + " + ".join(termos) + equacao_transformada = f"{y_label_transformada} = {lado_direito}" + # Montar a equação com a variável dependente na escala direta + equacao_revertida = f"{var_dep} = {ajustar_termo(lado_direito)}" + # Substituir pontos por vírgulas nas equações + equacao_transformada = equacao_transformada.replace('.', ',') + equacao_revertida = equacao_revertida.replace('.', ',') + # Adicionar as duas formas da equação aos resultados gerais + #resultados_gerais += f"\nEquação do modelo (variável dependente transformada): {equacao_transformada}" + resultados_gerais += f"\nEquação do modelo (variável dependente na escala direta): {equacao_revertida}" + + # Classificar variáveis com base nos p-valores + def classificar(valor): + if valor > 0.3: + return "Fora dos critérios" + elif valor > 0.2: + return "Grau I" + elif valor > 0.1: + return "Grau II" + else: + return "Grau III" + + resultados_vars['Classificação'] = resultados_vars['P>|t|'].apply(classificar) + + # Determinar grau único considerando todas as variáveis + def determinar_grau_unico(classificacoes): + if "Fora dos critérios" in classificacoes: + return "Fora dos critérios" + elif "Grau I" in classificacoes: + return "Grau I" + elif "Grau II" in classificacoes: + return "Grau II" + else: + return "Grau III" + + tab5 = determinar_grau_unico(resultados_vars['Classificação']) + + # Enquadramento na NBR 14.653-2 + # Item 2 da tabela + if num_observacoes >= 6 * (num_variaveis + 1): + tab2 = "Grau III" + elif num_observacoes >= 4 * (num_variaveis + 1): + tab2 = "Grau II" + elif num_observacoes >= 3 * (num_variaveis + 1): + tab2 = "Grau I" + else: + tab2 = "Fora dos critérios" + + # Item 6 da tabela + if nivel_significancia <= 0.01: + tab6 = "Grau III" + elif nivel_significancia <= 0.02: + tab6 = "Grau II" + elif nivel_significancia <= 0.05: + tab6 = "Grau I" + else: + tab6 = "Fora dos critérios" + + # Resultados gerais formatados + resultados_gerais = f""" + Desvio Padrão dos Resíduos: {desvio_padrao_residuos} + Estatística F: {estatistica_F} | Nível de Significância: {nivel_significancia} + R²: {r_squared} | R² Ajustado: {r_squared_adjusted} + Correlação: {coef_correlacao} + Número de Observações: {num_observacoes} + Número de Variáveis Independentes: {num_variaveis} + + Fundamentação - Quant. min. dados (Item 2 tab 9.2.1 NBR 14.653-2): {tab2} + Fundamentação - Signif. Regressores (Item 5 tab 9.2.1 NBR 14.653-2): {tab5} + Fundamentação - Signif. Modelo (Item 6 tab 9.2.1 NBR 14.653-2): {tab6} + + Testes de normalidade: + 1) Comparação (curva normal) - Percentuais atingidos: {perc_resid} + Ideal 68% - aceitável de 64% a 75% + Ideal 90% - aceitável de 88% a 95% + Ideal 95% - aceitável de 95% a 100% + + Teste Kolmogorov-Smirnov: Estatística = {ks_test_formatted[0]}, Valor-p = {ks_test_formatted[1]} - ({ks_interpretacao}) + + Distância de Cook (Máxima): {np.max(distancia_cook):.8f} + + Equação do modelo: {equacao_revertida} + """ + + # Adicionando a coluna de erro padronizado ao df_final + df_original_res = df_original.copy() + df_original_res['Erro Padronizado'] = erro_padronizado + + # Criar DataFrame apenas com os dados cujo erro padronizado é maior que 2 + df_grandes_residuos = df_original_res[abs(df_original_res['Erro Padronizado']) > 2].copy() + df_grandes_residuos['Erro Abs'] = abs(df_grandes_residuos['Erro Padronizado']) + + # Listagem de pontos com resíduos > 2 + listagem_grandes_residuos = ", ".join(map(str, df_grandes_residuos.iloc[:, 0].tolist())) + + # Listagem dos pontos influenciantes + limite_cook = 1 + pontos_influentes = [] + for i, cook_dist in enumerate(distancia_cook): + if cook_dist > limite_cook: + pontos_influentes.append(dados_transformados.iloc[i]["Dado"]) # Usando a primeira coluna como rótulo + + # Transformando a lista em uma string separada por vírgula + listagem_pontos_influentes = ", ".join(map(str, pontos_influentes)) + + # Criação de um dataframe para valores previstos + valores_previstos = modelo.predict(X) + valores_previstos_trans = valores_previstos.copy() + # Reverter a escala da variável dependente, se aplicável + if var_dep in escalas: + escala_var_dep = escalas[var_dep][0] # Obtém a escala associada + + if escala_var_dep == "ln(x)": + valores_previstos = np.exp(valores_previstos) # Reverte ln(x) para x + elif escala_var_dep == "1/(x)": + valores_previstos = 1 / valores_previstos # Reverte 1/(x) para x + elif escala_var_dep == "(x)²": + valores_previstos = np.sqrt(valores_previstos) # Reverte (x)² para x + elif escala_var_dep == "exp(x)": + valores_previstos = np.log(valores_previstos) # Reverte exp(x) para x + # Caso não seja necessário reverter, mantém os valores ajustados como estão + + # Adicionando os valores ajustados como uma nova coluna ao DataFrame original + df_calc_obs = df_original.copy() + df_calc_obs['Valores Ajustados'] = round(valores_previstos, 8) + # Resíduo + df_calc_obs['Resíduo'] = (df_calc_obs[var_dep].replace(0, np.nan) - df_calc_obs['Valores Ajustados']).round(4) + # Erro padronizado + df_calc_obs['Erro Padronizado'] = erro_padronizado.round(4) + # Adicionando a coluna de Erro + # Certifique-se de evitar divisão por zero + df_calc_obs['Erro'] = df_calc_obs['Valores Ajustados'] / df_calc_obs[var_dep].replace(0, np.nan) + # Arredondar os valores da coluna 'Erro' para melhorar a apresentação + df_calc_obs['Erro'] = df_calc_obs['Erro'].round(4) + # Criando a coluna de erro percentual + df_calc_obs['Erro Percentual (%)'] = (abs(df_calc_obs['Erro'] - 1) * 100).round(4) + + # Adicionando os valores ajustados como uma nova coluna ao DataFrame original + df_calc_obs_trans = dados_transformados.copy() + df_calc_obs_trans['Valores Ajustados'] = round(valores_previstos_trans, 8) + + return resultados_gerais, resultados_vars, df_grandes_residuos, listagem_grandes_residuos, listagem_pontos_influentes, df_calc_obs, df_calc_obs_trans, erro_padronizado, modelo + else: + return "Erro: A variável dependente não está nos dados transformados.", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None + except Exception as e: + return f"Erro na regressão: {str(e)}", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None + else: + return "Erro: Dados transformados inválidos ou vazios.", pd.DataFrame(), pd.DataFrame(), "Erro", "Erro", pd.DataFrame(), pd.DataFrame(), [], None + +# Função para plotar gráficos do modelo +def graficos(escala_dependente, df_calc_obs, df_calc_obs_trans, erro_padronizado, var_dep, num_bins=20): + + # Gráfico 1: Resíduos Padronizados + # Normalizar os resíduos padronizados para o colormap + residuos_norm = (np.abs(erro_padronizado) - np.abs(erro_padronizado).min()) / \ + (np.abs(erro_padronizado).max() - np.abs(erro_padronizado).min()) + + fig1 = go.Figure() + fig1.add_trace(go.Scatter( + x=df_calc_obs_trans['Valores Ajustados'], + y=erro_padronizado, + mode='markers', + marker=dict( + size=8, + color=residuos_norm, # Aplicar o colorscale + colorscale='Spectral', # Escolher a paleta de cores + ), + text=df_calc_obs_trans.iloc[:, 0], + hovertemplate="Índice: %{text}
Valores Ajustados: %{x:.2f}
Resíduos: %{y:.2f}" + )) + fig1.add_hline(y=0, line_dash="dash", line_color="black") + fig1.add_hline(y=2, line_dash="dot", line_color="red") + fig1.add_hline(y=-2, line_dash="dot", line_color="red") + fig1.update_layout( + title="Gráfico de Resíduos Padronizados", + xaxis_title="Valores Ajustados", + yaxis_title="Resíduos Padronizados", + template="plotly_white" + ) + + # Gráfico 2: Histograma dos Resíduos Padronizados com Curva Normal + # Calcula média e desvio padrão dos resíduos + mean_residuos = np.mean(erro_padronizado) + std_residuos = np.std(erro_padronizado) + + # Dados para a curva normal + x_vals = np.linspace(mean_residuos - 4 * std_residuos, mean_residuos + 4 * std_residuos, 500) + y_vals = stats.norm.pdf(x_vals, mean_residuos, std_residuos) # PDF da curva normal + + # Criar o histograma (frequência normalizada) + hist_values, bin_edges = np.histogram(erro_padronizado, bins=num_bins, density=True) + scale_factor = max(hist_values) / max(y_vals) # Ajustar altura da curva normal + y_vals_scaled = y_vals * scale_factor + + # Calcular o valor médio dos resíduos absolutos para cada bin + bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2 + bin_colors = 1 - (np.abs(bin_centers) - np.abs(bin_centers).min()) / (np.abs(bin_centers).max() - np.abs(bin_centers).min()) + + fig2 = go.Figure() + fig2.add_trace(go.Bar( + x=bin_centers, # Centraliza as barras + y=hist_values, + width=np.diff(bin_edges), + marker=dict( + color=bin_colors, # Aplicar o colorscale + colorscale='Reds', + ), + opacity=0.7, + name='Histograma' + )) + + # Adicionar a curva normal ajustada + fig2.add_trace(go.Scatter( + x=x_vals, + y=y_vals_scaled, + mode='lines', + line=dict(color='red', width=2), + name='Curva Normal' + )) + + fig2.update_layout( + title="Histograma dos Resíduos Padronizados com Curva Normal", + xaxis_title="Resíduos Padronizados", + yaxis_title="Frequência Normalizada", + template="plotly_white", + ) + + # Gráfico 3: Valores Ajustados vs Preços Observados + if escala_dependente == "Direta": + df_graf_ao = df_calc_obs + else: + df_graf_ao = df_calc_obs_trans + + # Definir os eixos + valores_observados = df_graf_ao.iloc[:, 1] # Segunda coluna do DataFrame + valores_calculados = df_graf_ao['Valores Ajustados'] + + # Cálculo dos resíduos normalizados + residuos = np.abs(valores_observados - valores_calculados) + residuos_norm = (residuos - residuos.min()) / (residuos.max() - residuos.min()) # Normalizar para o colorscale + + # Ajustar a reta de regressão linear com statsmodels + X = sm.add_constant(valores_observados) # Adicionar uma constante (intercepto) + modelo = sm.OLS(valores_calculados, X).fit() # Ajustar o modelo OLS + x_reta = np.linspace(valores_observados.min(), valores_observados.max(), 100) # Valores de X para a reta + y_reta = modelo.predict(sm.add_constant(x_reta)) # Predizer Y com base no modelo + + # Criar o gráfico + fig3 = go.Figure() + fig3.add_trace(go.Scatter( + x=valores_observados, + y=valores_calculados, + mode='markers', + marker=dict( + size=8, + color=residuos_norm, # Aplicar o colorscale + colorscale='Spectral', # Escolher a paleta de cores + showscale=False + ), + text=df_graf_ao.iloc[:, 0], # Índice ou outra coluna para hover + hovertemplate="Índice: %{text}
Observado: %{x:.2f}
Ajustado: %{y:.2f}", + showlegend=False # Remover legenda deste trace + )) + + # Adicionar a reta ajustada (linha de regressão) + fig3.add_trace(go.Scatter( + x=x_reta, + y=y_reta, + mode="lines", + line=dict(color="green", dash="solid"), + name="Reta Ajustada" + )) + + # Configuração do layout + fig3.update_layout( + title="Valores Ajustados vs Preços Observados", + xaxis_title="Preços Observados", + yaxis_title="Valores Ajustados", + template="plotly_white", + showlegend=False # Remover legenda + ) + + + + # Gráfico 4: Matriz de Correlações + corr_matrix = df_calc_obs_trans.drop(columns=["Dado", "Valores Ajustados"], errors='ignore').corr() + + # Criar o Heatmap diretamente com texto + fig4 = go.Figure() + + # Adicionar os valores manualmente como anotação no gráfico + for i, row in enumerate(corr_matrix.index): + for j, col in enumerate(corr_matrix.columns): + # Determinar a cor do texto com base nas condições + if row == col: + color = "black" # Preto para a diagonal (mesma variável) + elif corr_matrix.loc[row, col] > 0.8: # Correlação acima de 0.8 + if row == var_dep or col == var_dep: + color = "blue" #tr Azul para correlação com var_dep + else: + color = "red" # Vermelho para correlação alta entre variáveis independentes + else: + color = "black" # Preto para todas as outras correlações + + # Adicionar o texto no gráfico + fig4.add_trace(go.Scatter( + x=[col], + y=[row], + text=[f"{corr_matrix.loc[row, col]:.2f}"], + mode="text", + textfont=dict(size=12, color=color), # Aplicar a cor + )) + + # Atualizar o layout + fig4.update_layout( + title="Matriz de Correlações", + xaxis=dict(title="Variáveis", tickmode='array', tickvals=corr_matrix.columns), + yaxis=dict(title="Variáveis", tickmode='array', tickvals=corr_matrix.index), + template="plotly_white", + showlegend=False + ) + + return fig1, fig2, fig3, fig4 + +# Função para exportar para o excel +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): + try: + # Criar um arquivo Excel com múltiplas abas + with pd.ExcelWriter(nome_arquivo, engine='xlsxwriter') as writer: + # Adicionar os DataFrames em abas específicas + df_planilha.to_excel(writer, sheet_name='Dados Originais', index=False) + df_infos.to_excel(writer, sheet_name='Infos', index=False) + df_original.to_excel(writer, sheet_name='Dados Modelo', index=False) + #df_escalado.to_excel(writer, sheet_name='Dados Modelo Transformados', index=False) + df_outliers.to_excel(writer, sheet_name='Outliers', index=False) + + # Converter "Resultados Gerais" para um DataFrame formatado + resultados_lista = [linha.strip() for linha in resultados_gerais.split("\n") if linha.strip()] + resultados_df = pd.DataFrame({"Descrição": resultados_lista}) + resultados_df.to_excel(writer, sheet_name='Resultados Gerais', index=False) + + # Resultados por variável + resultados_vars.to_excel(writer, sheet_name='Resultados Variáveis', index=False) + + # Valores calculados x observados + df_calc_obs.to_excel(writer, sheet_name='Calc x Obs', index=False) + + # Valores calculados transformados + #df_calc_obs_trans.to_excel(writer, sheet_name='Calculados Transformados', index=False) + + return f"Arquivo '{nome_arquivo}' criado com sucesso." + except Exception as e: + return f"Erro ao criar o arquivo Excel: {str(e)}" + +# Função para salvar o modelo +def exportar_modelo_completo_avse(nome_pacote, modelo, resultados_gerais, df_planilha, df_infos, df_original, df_outliers, resultados_vars, df_calc_obs): + try: + # Verificar se o nome do pacote está vazio + if not nome_pacote: + return "Erro: O nome do arquivo não pode estar vazio." + + # Garantir que o nome do pacote tenha a extensão .avse + if not nome_pacote.endswith(".avse"): + nome_pacote += ".avse" + + # Empacotar todos os itens em um dicionário + pacote = { + "modelo": modelo, + "Resultados Gerais": resultados_gerais, + "df_planilha": df_planilha, + "df_infos": df_infos, + "df_original": df_original, + "df_outliers": df_outliers, + "resultados_vars": resultados_vars, + "df_calc_obs": df_calc_obs + } + + # Salvar o pacote usando joblib + dump(pacote, nome_pacote) + return f"Pacote '{nome_pacote}' criado com sucesso." + except Exception as e: + return f"Erro ao criar o pacote: {str(e)}" + +# Função para carregar o modelo +def carregar_modelo(nome_pacote): + try: + # Carregar o pacote salvo + pacote = joblib.load(nome_pacote) + + # Recuperar os objetos do pacote + modelo = pacote.get("modelo", None) # Objeto do modelo salvo + resultados_gerais = pacote.get("Resultados Gerais", "") # Resultados gerais do modelo + df_planilha = pacote.get("df_planilha", pd.DataFrame()) + df_infos = pacote.get("df_infos", pd.DataFrame()) + df_original = pacote.get("df_original", pd.DataFrame()) + df_outliers = pacote.get("df_outliers", pd.DataFrame()) + resultados_vars = pacote.get("resultados_vars", pd.DataFrame()) + df_calc_obs = pacote.get("df_calc_obs", pd.DataFrame()) + + # Adicionar uma linha em branco no df_infos se ele não estiver vazio + if not df_infos.empty: + df_infos.loc[len(df_infos)] = [None] * len(df_infos.columns) + + # Transpor o DataFrame + df_infos_transposed = df_infos.T + + # Renomear as colunas + df_infos_transposed.columns = ['Escalas', 'Máximo', 'Mínimo', 'Médio', 'Avaliando'] + + # Adicionar a coluna 'Variáveis' com os nomes das colunas originais + df_infos_transposed.insert(0, 'Variáveis', df_infos.columns) + + # Atribuir o valor padrão "------" à célula da primeira linha da última coluna + df_infos_transposed.iloc[0, -1] = "Variável dependente" + + # Obter apenas o nome do arquivo, sem o caminho + nome_arquivo = os.path.basename(nome_pacote) + + # Criar mensagem de sucesso com o nome do modelo + mensagem_sucesso = f"Modelo '{nome_arquivo}' carregado com sucesso." + + # Retornar todos os componentes necessários + return ( + mensagem_sucesso, # Mensagem de sucesso com o nome do modelo + modelo, # Modelo carregado para previsões + resultados_gerais, # Resultados gerais do modelo + df_planilha, # Dados originais + df_original, # Dados usados no modelo + df_outliers, # Outliers identificados + resultados_vars, # Resultados por variável + df_calc_obs, # Dados calculados x observados + df_infos_transposed, # Informações adicionais do modelo + ) + except Exception as e: + # Retornar valores padrão em caso de erro + return ( + "Carregue o modelo antes de clicar no botão VISUALIZAR MODELO", + None, # Modelo não carregado + "", # Resultados gerais vazio + pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), + ) + + + +def realizar_previsoes(modelo, tab_infos_carregada): + try: + if modelo is None or tab_infos_carregada.empty: + return "Erro: Modelo não carregado ou tabela de informações vazia.", pd.DataFrame(), pd.DataFrame(), "" + + # Identificar as escalas das variáveis independentes e dependente + escalas_independentes = tab_infos_carregada.iloc[1:, 1].to_list() # a partir da 2ª linha da 2ª coluna + escala_dependente = tab_infos_carregada.iloc[0, 1] # 1ª linha da 2ª coluna + + # Valores para previsão estão a partir da 2ª linha da 6ª coluna + valores_entrada = tab_infos_carregada.iloc[1:, 5].values + + # Converter valores para numéricos, substituindo inválidos por NaN + valores_entrada = pd.to_numeric(valores_entrada, errors='coerce') + + # Aplicar escalas às variáveis independentes + valores_transformados = [] + for valor, escala in zip(valores_entrada, escalas_independentes): + if np.isnan(valor): + valores_transformados.append(np.nan) # Manter NaN se o valor for inválido + elif escala == "ln(x)": + valores_transformados.append(np.log(valor) if valor > 0 else np.nan) # Evitar log de zero ou negativo + elif escala == "1/(x)": + valores_transformados.append(1 / valor if valor != 0 else np.nan) # Evitar divisão por zero + elif escala == "(x)²": + valores_transformados.append(valor ** 2) + elif escala == "exp(x)": + valores_transformados.append(np.exp(valor)) + else: + valores_transformados.append(valor) + + # Transformar em DataFrame para manter o formato necessário + df_previsao = pd.DataFrame([valores_transformados], columns=tab_infos_carregada.iloc[1:, 0]) + + # Adicionar constante ao modelo para previsão + X = sm.add_constant(df_previsao, has_constant='add') + + # Garantir que as colunas em X correspondam às usadas no modelo + colunas_modelo = modelo.model.exog_names + X = X.reindex(columns=colunas_modelo, fill_value=0) # Reordenar e preencher colunas faltantes com 0 + + # Verificar se existem valores NaN nos dados transformados + if X.isnull().values.any(): + return "Erro: Existem valores inválidos após a transformação. Verifique os dados de entrada.", df_previsao, pd.DataFrame(), "" + + # Realizar a previsão + previsao_transformada = modelo.predict(X).iloc[0] + + # Ajustar para a escala direta da variável dependente + if escala_dependente == "ln(x)": + previsao_final = np.exp(previsao_transformada) + elif escala_dependente == "1/(x)": + previsao_final = 1 / previsao_transformada + elif escala_dependente == "(x)²": + previsao_final = np.sqrt(previsao_transformada) + elif escala_dependente == "exp(x)": + previsao_final = np.log(previsao_transformada) + else: + previsao_final = previsao_transformada + + # Calcular limites do campo de arbítrio + limite_inferior_arbitrio = previsao_final * 0.85 + limite_superior_arbitrio = previsao_final * 1.15 + + # Calcular intervalo de confiança de 80% + intervalos = modelo.get_prediction(X).conf_int(alpha=0.2) + limite_inferior_ic = intervalos[0, 0] # Acessar diretamente o valor do array + limite_superior_ic = intervalos[0, 1] # Acessar diretamente o valor do array + + # Ajustar limites de IC para a escala direta, se necessário + if escala_dependente == "ln(x)": + limite_inferior_ic = np.exp(limite_inferior_ic) + limite_superior_ic = np.exp(limite_superior_ic) + elif escala_dependente == "1/(x)": + limite_inferior_ic = 1 / limite_inferior_ic + limite_superior_ic = 1 / limite_superior_ic + elif escala_dependente == "(x)²": + limite_inferior_ic = np.sqrt(limite_inferior_ic) + limite_superior_ic = np.sqrt(limite_superior_ic) + elif escala_dependente == "exp(x)": + limite_inferior_ic = np.log(limite_inferior_ic) + limite_superior_ic = np.log(limite_superior_ic) + + # Cálculo da extrapolação + extrapolacao_info = "" + valores_ajustados = valores_entrada.copy() + contagem_extrapolacoes = 0 + fora_dos_criterios = False + for i, valor in enumerate(valores_entrada): + maximo = pd.to_numeric(tab_infos_carregada.iloc[i+1, 2], errors='coerce') # Coluna 3 (Mínimo) + minimo = pd.to_numeric(tab_infos_carregada.iloc[i+1, 3], errors='coerce') # Coluna 4 (Máximo) + + if not np.isnan(valor): + if valor < minimo: + percentual_extrapolacao = ((minimo - valor) / minimo) * -100 + extrapolacao_info += f"Variável '{tab_infos_carregada.iloc[i+1, 0]}' está {percentual_extrapolacao:.2f}% abaixo do mínimo da amostra.\n" + valores_ajustados[i] = minimo # Ajustar valor para o mínimo + contagem_extrapolacoes += 1 + if percentual_extrapolacao < -50: + fora_dos_criterios = True + elif valor > maximo: + percentual_extrapolacao = ((valor - maximo) / maximo) * 100 + extrapolacao_info += f"Variável '{tab_infos_carregada.iloc[i+1, 0]}' está {percentual_extrapolacao:.2f}% acima do máximo da amostra.\n" + valores_ajustados[i] = maximo # Ajustar valor para o máximo + contagem_extrapolacoes += 1 + if percentual_extrapolacao > 100: + fora_dos_criterios = True + + # Criar novos DataFrames para cada variável que extrapolou + previsoes_extrapoladas = [] + for i, (valor, ajustado) in enumerate(zip(valores_entrada, valores_ajustados)): + if valor != ajustado: + valores_transformados_ext = [] + for valor_ext, escala in zip(valores_ajustados, escalas_independentes): + if np.isnan(valor_ext): + valores_transformados_ext.append(np.nan) + elif escala == "ln(x)": + valores_transformados_ext.append(np.log(valor_ext) if valor_ext > 0 else np.nan) + elif escala == "1/(x)": + valores_transformados_ext.append(1 / valor_ext if valor_ext != 0 else np.nan) + elif escala == "(x)²": + valores_transformados_ext.append(valor_ext ** 2) + elif escala == "exp(x)": + valores_transformados_ext.append(np.exp(valor_ext)) + else: + valores_transformados_ext.append(valor_ext) + + df_previsao_ext = pd.DataFrame([valores_transformados_ext], columns=tab_infos_carregada.iloc[1:, 0]) + X_ext = sm.add_constant(df_previsao_ext, has_constant='add') + X_ext = X_ext.reindex(columns=colunas_modelo, fill_value=0) + + if X_ext.isnull().values.any(): + continue + + previsao_transformada_ext = modelo.predict(X_ext).iloc[0] + + if escala_dependente == "ln(x)": + previsao_final_ext = np.exp(previsao_transformada_ext) + elif escala_dependente == "1/(x)": + previsao_final_ext = 1 / previsao_transformada_ext + elif escala_dependente == "(x)²": + previsao_final_ext = np.sqrt(previsao_transformada_ext) + elif escala_dependente == "exp(x)": + previsao_final_ext = np.log(previsao_transformada_ext) + else: + previsao_final_ext = previsao_transformada_ext + + previsoes_extrapoladas.append((df_previsao_ext, previsao_final_ext)) + + # Determinar a fundamentação da extrapolação + if fora_dos_criterios: + fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Fora dos critérios" + elif contagem_extrapolacoes == 1: + fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Grau II" + elif contagem_extrapolacoes > 1: + fundamentacao = "Fundamentação - Extrapolação (Item 4 (a) tab 9.2.1 NBR 14.653-2): Grau I" + else: + fundamentacao = "" + + # Retornar as informações + resultado_texto = ( + f"Valor estimado central: {locale.currency(previsao_final, grouping=True)}\n" + "------------------------------------\n" + f"Limite inferior do campo de arbítrio (- 15% do Valor estimado central): {locale.currency(limite_inferior_arbitrio, grouping=True)}\n" + f"Limite superior do campo de arbítrio (+ 15% do Valor estimado central): {locale.currency(limite_superior_arbitrio, grouping=True)}\n" + "------------------------------------\n" + f"Limite inferior do Intervalo de Confiança de 80%: {locale.currency(limite_inferior_ic, grouping=True)}\n" + f"Limite superior do Intervalo de Confiança de 80%: {locale.currency(limite_superior_ic, grouping=True)}\n" + "------------------------------------\n" + f"{extrapolacao_info.strip()}\n" + f"{fundamentacao}\n" + ) + + + resultado_df = df_previsao + resultado_df_ext = pd.DataFrame() + resultado_texto_ext = "" + + if previsoes_extrapoladas: + for df_ext, previsao_ext in previsoes_extrapoladas: + resultado_df_ext = df_ext + resultado_texto_ext = ( + f"Valor estimado central (na fronteira amostral): {locale.currency(previsao_ext, grouping=True)}\n" + "------------------------------------\n" + ) + + # Cálculo da variação percentual + variacao_percentual = abs((previsao_final - previsao_ext) / previsao_final) * 100 + + # Determinar a fundamentação com base na variação percentual + if variacao_percentual > 20: + fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Fora dos critérios" + elif variacao_percentual > 15: + fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Grau I" + else: + fundamentacao_ext = "Fundamentação - Extrapolação (Item 4 (b) tab 9.2.1 NBR 14.653-2): Grau II" + + resultado_texto_ext += f"{fundamentacao_ext}\n" + + return resultado_df, resultado_texto, resultado_df_ext, resultado_texto_ext + + except Exception as e: + return pd.DataFrame(), "Erro ao realizar a previsão: {str(e)}", pd.DataFrame(), "" + + +#--------------------------------------------INTERFACE-------------------------------------------# + +def rl_tab(): + # Criação da aba + with gr.Tab("Regressão Linear"): + planilha_input = gr.File(label="Carregar Planilha", file_types=[".xls", ".xlsx"], elem_classes=["small-file-upload"]) + + with gr.Group(): + + #---------ESTADOS DE ARMAZENAMENTO-------------# + + # Estado para armazenamentos + cabeçalhos_state = gr.State([]) + dados_state = gr.State(pd.DataFrame()) + escalas_state = gr.State({}) + erro_padronizado_state = gr.State([]) + # Estados para armazenar os gráficos Plotly gerados + grafico_residuos_state = gr.State(None) + grafico_histograma_state = gr.State(None) + grafico_ajustados_state = gr.State(None) + matriz_corr_state = gr.State(None) + # Estado para armazenar o modelo gerado + modelo_state = gr.State(None) + + #---------PLANILHA DE ENTRADA-------------# + + # Checkbox para controlar a visibilidade da planilha carregada + 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) + tabela_planilha = gr.Dataframe(visible=False, max_height=250, elem_classes=["small span"]) # Oculto por padrão + + #---------GRÁFICOS DE DISPERSÃO-------------# + + # Gráficos de dispersão + with gr.Row(equal_height=True): + coluna_y_dispersao = gr.Dropdown(label="Eixo Y (Dispersão)", choices=[], interactive=True, visible=False, scale=3) + transformacao_y = gr.Dropdown( + label="Transformação para Eixo Y", + choices=["Nenhuma", "1/x", "ln(x)", "x²", "exp(x)"], + value="Nenhuma", + interactive=True, + visible=False, + scale=2 + ) + coluna_x_dispersao = gr.Dropdown(label="Eixo X (Dispersão)", choices=[], interactive=True, visible=False, scale=3) + transformacao_x = gr.Dropdown( + label="Transformação para Eixo X", + choices=["Nenhuma", "1/x", "ln(x)", "x²", "exp(x)"], + value="Nenhuma", + interactive=True, + visible=False, + scale=2 + ) + # Adicionando os gráficos de dispersão e boxplot lado a lado com o mesmo botão + botao_dispersao_boxplot = gr.Button("Gerar Gráficos de Dispersão e Boxplot", visible=False) + with gr.Row(): + grafico_dispersao_saida = gr.Plot(label="Gráfico de Dispersão", visible=False, scale=3) + grafico_boxplot_saida = gr.Plot(label="Gráfico Boxplot", visible=False, scale=1) + + #---------ESCOLHA DAS VARIÁVEIS-------------# + + # Checkbox para controlar a visibilidade dos dropdowns + mostrar_dropdowns = gr.Checkbox(label="VARIÁVEIS E ESCALAS (Variável dependente e variáveis independentes)", value=False, elem_classes="checkbox-yellow") + # Dropdown multi-select para cabeçalhos + # Título do conjunto + with gr.Group(): + 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 + with gr.Row(): + colunas_x = gr.Dropdown(label="(Direta: y ou x)", multiselect=True, choices=[], value=[], interactive=True, visible=False) + colunas_ln_x = gr.Dropdown(label="Logarítmica: ln(y) ou ln(x)", multiselect=True, choices=[], interactive=True, visible=False) + colunas_exp_x = gr.Dropdown(label="Exponencial: exp(y) ou exp(x)", multiselect=True, choices=[], interactive=True, visible=False) + colunas_inv_x = gr.Dropdown(label="Inversa: 1/(y) ou 1/(x)", multiselect=True, choices=[], interactive=True, visible=False) + colunas_quad_x = gr.Dropdown(label="Quadrática: (x)² ou (y²) ", multiselect=True, choices=[], interactive=True, visible=False) + # Escolha da variável dependente + var_dep = gr.Dropdown(label="Variável Dependente", multiselect=False, choices=[], interactive=True, visible=False) + + #---------DATAFRAMES-------------# + + with gr.Row(equal_height=True): + # Botão para gerar DataFrames + botao_gerar_df = gr.Button("Gerar Planilhas", scale=1) + # Checkbox para controlar visibilidade dos DataFrames gerados + mostrar_dataframes = gr.Checkbox(label="MOSTRAR PLANILHAS (Escalas e limites, Variáveis transformadas e Outliers)", value=False, scale=3) + # DataFrames para visualização + 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"]) + tabela_original = gr.Dataframe(label="Planilha original com Variáveis Escolhidas", max_height=250, visible=False, elem_classes=["small span"]) + tabela_escalada = gr.Dataframe(label="Planilha com Variáveis Transformadas", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"]) + #matriz_correl = gr.Dataframe(label="Matriz de correlações", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"]) + df_out = gr.Dataframe(label="Outliers", max_height=150, visible=False, interactive=True, headers=None, elem_classes=["small span"]) + + #---------MODELO-------------# + + with gr.Row(equal_height=True): + # Adicionando botão para executar a regressão + botao_regressao = gr.Button("Gerar Modelo", scale=1) + # Checkbox para visualizar os resultados + 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) + # Resultados gerais e por variáveis + with gr.Row(): + resultado_geral = gr.Textbox(label="Resultados Gerais", lines=25, interactive=False, visible=False, scale=1) + resultado_coef = gr.Dataframe(label="Resultados por Variável", interactive=False, visible=False, scale=1, elem_classes=["small span"]) + # Resíduos (dataframe e listagem) + residuos = gr.Dataframe(label="Resíduos padronizados > 2", interactive=False, max_height=250, visible=False, scale=1, elem_classes=["small span"]) + with gr.Row(): + # Resíduos > 2 + residuos_list = gr.Textbox(label="Listagem resíduos padronizados > 2", lines=2, interactive=False, visible=False, scale=2) + # Pontos influenciantes + pontos_inf = gr.Textbox(label="Listagem dos Pontos Influenciantes", lines=2, interactive=False, visible=False, scale=1) + # Entrada dinâmica + 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", + interactive=True, visible=False, scale=2) + + # Adicionando a visualização para df_calc_obs + calc_obs = gr.Dataframe(label="Calculados x Observados", max_height=250, visible=False, interactive=True, headers=None, elem_classes=["small span"]) + calc_obs_trans = gr.Dataframe(label="Calculados Transformados", max_height=250, visible=False, interactive=True, elem_classes=["small span"]) + + #---------GRÁFICOS DO MODELO-------------# + + with gr.Row(equal_height=True): + # Adicionar botão para gerar gráficos + botao_graficos = gr.Button("Gerar Gráficos", scale=1) + # Checkbox para controlar a visibilidade da seção de gráficos do modelo + 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) + # Escolha a escala da variável dependente para o gráfico Ajustados x Observado + escala_graf_ao = gr.Dropdown(label="Escolha a escala da variável dependente para o gráfico Valores Ajustados x Observado", choices=["Transformada", "Direta"]) + + # Adicionar rádio para selecionar o gráfico + grafico_selecao = gr.Radio( + choices=["Resíduos Padronizados", "Histograma", "Valores Ajustados vs Observados", "Matriz de Correlações"], + label="Selecione o Gráfico", + interactive=True, + visible=False + ) + + # Adicionar saída para o gráfico selecionado + grafico_saida = gr.Plot(label="Gráfico Selecionado", visible=False) + + #---------EXPORTAR PARA O EXCEL-------------# + + with gr.Row(equal_height=True): + botao_exportar_excel = gr.Button("Exportar Planilha") + arquivo_excel = gr.File(label="Baixar Excel", interactive=False, visible=False) + + #---------SALVAR MODELO-------------# + + with gr.Row(equal_height=True): + # Adicionar campo de entrada para o nome do arquivo + nome_arquivo_modelo = gr.Textbox( + label="Nome do Arquivo do Modelo", + placeholder="Digite o nome do modelo e clique no botão ao lado para salvar", + interactive=True, + scale=3 + ) + # Adicionar botão "SALVAR MODELO" e output para indicar o sucesso/erro + botao_salvar_modelo = gr.Button("Salvar Modelo", scale=1) + saida_salvar_modelo = gr.Textbox( + label="Status do Salvamento do Modelo", + interactive=False, + visible=False, + lines=2 + ) + + #---------CARREGAR MODELO-------------# + + with gr.Row(equal_height=True): + # Entrada para carregar o modelo + modelo_input = gr.File(label="Carregar Modelo", file_types=[".avse"], elem_classes=["small-file-upload"]) + # Botão para carregar o modelo + botao_carregar_modelo = gr.Button("Visualizar Modelo") + # Checkbox abaixo do botão "Visualizar Modelo" + mostrar_modelo_checkbox = gr.Checkbox( + label="MOSTRAR DETALHES DO MODELO", + value=False, + elem_classes=["checkbox-yellow"] + ) + # Mensagem de status para aparecer logo abaixo do modelo_input + status_carregamento = gr.Textbox( + label="Status do Carregamento do Modelo", + interactive=False, + visible=False, # Certifique-se de que está visível + lines=1, + elem_classes=["small span"] + ) + + # TextBox para exibir os resultados gerais + resultado_geral_carregado = gr.Textbox( + label="Resultados Gerais (Carregados)", + interactive=False, + visible=False, + lines=25, # Ajuste o número de linhas conforme necessário + elem_classes=["small span"] + ) + + # DataFrames para exibição + tabela_planilha_carregada = gr.Dataframe(label="Dados Originais", visible=False, max_height=250, elem_classes=["small span"]) + tabela_original_carregada = gr.Dataframe(label="Dados Modelo", visible=False, max_height=250, elem_classes=["small span"]) + tabela_outliers_carregada = gr.Dataframe(label="Outliers", visible=False, max_height=250, elem_classes=["small span"]) + tabela_resultados_vars_carregada = gr.Dataframe(label="Resultados Variáveis", visible=False, max_height=250, elem_classes=["small span"]) + tabela_calc_obs_carregada = gr.Dataframe(label="Calculados x Observados", visible=False, max_height=250, elem_classes=["small span"]) + tab_infos_carregada = gr.Dataframe(label="Variáveis, escalas, limites e avaliando", interactive=True, visible=False, elem_classes=["small span"]) + + #---------AVALIAÇÃO-------------# + + botao_previsao = gr.Button("Realizar Previsão") + tabela_previsao = gr.Dataframe(label="Valores Transformados para Previsão", visible=True, max_height=250) + previsao_saida = gr.Textbox(label="Resultado da Previsão", lines=8, interactive=False, visible=True) + + # Adicionar DataFrames para exibir os resultados das previsões ajustadas + tabela_previsao_ajustada = gr.Dataframe(label="Valores Transformados para Previsão Ajustada", visible=True, max_height=250) + previsao_ajustada_saida = gr.Textbox(label="Resultado da Previsão Ajustada", lines=8, interactive=False, visible=True) + + # 1. CONTROLE DE VISIBILIDADE + + # 1.1. Mostrar ou ocultar a planilha carregada e os gráficos + mostrar_planilha.change( + lambda visible, dados: [gr.update(visible=visible, value=dados)] * 8, + inputs=[mostrar_planilha, dados_state], + outputs=[ + tabela_planilha, coluna_y_dispersao, transformacao_y, coluna_x_dispersao, + transformacao_x, botao_dispersao_boxplot, + grafico_dispersao_saida, grafico_boxplot_saida + ] + ) + + # 1.2. Mostrar ou ocultar os dropdowns para escolha de variáveis transformadas + mostrar_dropdowns.change( + lambda visible: [gr.update(visible=visible)] * 7, + inputs=[mostrar_dropdowns], + outputs=[var_ind, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, var_dep] + ) + + # 1.3. Mostrar ou ocultar os DataFrames gerados (cabeçalhos, escalas, correlação, etc.) + mostrar_dataframes.change( + lambda visible: [gr.update(visible=visible)] * 3, + inputs=[mostrar_dataframes], + outputs=[tabela_cabecalhos, tabela_escalada, df_out] #matriz_correl, + ) + + # 1.4. Mostrar ou ocultar os resultados da regressão (resultados gerais, resíduos, etc.) + mostrar_resultados.change( + lambda visible: [gr.update(visible=visible)] * 7, + inputs=[mostrar_resultados], + outputs=[resultado_geral, resultado_coef, residuos, residuos_list, pontos_inf, entrada_dinamica, calc_obs] + ) + + # 1.5. Mostrar ou ocultar a seção de gráficos e componentes associados + mostrar_graficos.change( + lambda visible: [gr.update(visible=visible)] * 2, + inputs=[mostrar_graficos], + outputs=[grafico_selecao, grafico_saida] + ) + + # 1.6. Mostrar ou ocultar os detalhes do modelo carregado + mostrar_modelo_checkbox.change( + lambda visible: [gr.update(visible=visible)] * 8, + inputs=[mostrar_modelo_checkbox], + outputs=[ + resultado_geral_carregado, tabela_planilha_carregada, tabela_original_carregada, + tabela_outliers_carregada, tabela_resultados_vars_carregada, tabela_calc_obs_carregada, + tab_infos_carregada, status_carregamento + ] + ) + + # 2. CARREGAMENTO DE PLANILHA E ATUALIZAÇÃO DE DADOS + + # 2.1. Carregar a planilha e extrair cabeçalhos e dados + planilha_input.change( + lambda file: carregar_planilha(file), + inputs=[planilha_input], + outputs=[cabeçalhos_state, dados_state] + ) + + # 2.2. Atualizar as opções de dropdown para o gráfico de dispersão ao carregar uma planilha + cabeçalhos_state.change( + lambda cabecalhos: [gr.update(choices=cabecalhos, value=None)] * 2, + inputs=[cabeçalhos_state], + outputs=[coluna_y_dispersao, coluna_x_dispersao] + ) + + # 2.3. Atualizar os dropdowns para transformações e seleção de variáveis dependentes/independentes + cabeçalhos_state.change( + atualizar_dropdowns, + inputs=[cabeçalhos_state], + outputs=[colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, var_dep] + ) + + # 2.4. Atualizar o gráfico exibido com base na seleção do usuário + grafico_selecao.change( + lambda escala, selecao, fig1, fig2, fig3, fig4: ( + fig1 if selecao == "Resíduos Padronizados" else + (fig2 if selecao == "Histograma" else + (fig3 if selecao == "Valores Ajustados vs Observados" else fig4)) + ), + inputs=[escala_graf_ao, grafico_selecao, grafico_residuos_state, grafico_histograma_state, grafico_ajustados_state, matriz_corr_state], + outputs=[grafico_saida] + ) + + # 3. BOTÕES + # 3.1. Botão para conectar à função de Gerar os gráficos de dispersão e boxplot (GERAR DISPERSÃO E BOXPLOT) + botao_dispersao_boxplot.click( + lambda df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out: ( + grafico_dispersao(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out), + grafico_boxplot(df, y_coluna, transformacao_y, x_coluna, transformacao_x, dados_out) + ), + inputs=[dados_state, coluna_y_dispersao, transformacao_y, coluna_x_dispersao, transformacao_x, entrada_dinamica], + outputs=[grafico_dispersao_saida, grafico_boxplot_saida] + ) + + # 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 + # transformados, escalados e correlação (GERAR PLANILHAS) + botao_gerar_df.click( + criar_dataframe_cabecalhos, + inputs=[cabeçalhos_state, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, dados_state, entrada_dinamica, var_dep], + outputs=[tabela_cabecalhos, escalas_state] + ) + + botao_gerar_df.click( + criar_dataframes, + inputs=[dados_state, colunas_x, colunas_ln_x, colunas_exp_x, colunas_inv_x, colunas_quad_x, entrada_dinamica, var_dep], + outputs=[tabela_original, tabela_escalada, df_out] # , matriz_correl + ) + + # 3.3. Botão para conectar à função de realizar regressão linear com base nas variáveis selecionadas (GERAR MODELO) + botao_regressao.click( + realizar_regressao, + inputs=[var_dep, tabela_escalada, tabela_original, escalas_state], + outputs=[resultado_geral, resultado_coef, residuos, residuos_list, pontos_inf, calc_obs, calc_obs_trans, + erro_padronizado_state, modelo_state] + ) + + # 3.4. Botão para conectar à função de Gerar gráficos do modelo (GERAR GRÁFICOS - resíduos, histograma, ajustados vs observados) + botao_graficos.click( + graficos, # Função que gera os gráficos Plotly + inputs=[escala_graf_ao, calc_obs, calc_obs_trans, erro_padronizado_state, var_dep], + outputs=[grafico_residuos_state, grafico_histograma_state, grafico_ajustados_state, matriz_corr_state] + ) + + # 3.5. Botão para conectar à função de exportar para o excel (EXPORTAR PLANILHA) + botao_exportar_excel.click( + lambda df_planilha, df_infos, df_original, df_escalado, df_out, resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans: ( + "resultado.xlsx", + exportar_para_excel( + "resultado.xlsx", df_planilha, df_infos, df_original, df_escalado, df_out, + resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans + ) + )[0], + inputs=[ + dados_state, tabela_cabecalhos, tabela_original, tabela_escalada, df_out, + resultado_geral, resultado_coef, calc_obs, calc_obs_trans + ], + outputs=[arquivo_excel] + ) + # Atualizar visibilidade do status após clique no botão + botao_exportar_excel.click( + lambda *args: gr.update(visible=True), + inputs=[], + outputs=[arquivo_excel] + ) + + # 3.6. Botão para conectar à função de salvar o modelo (SALVAR MODELO) + botao_salvar_modelo.click( + lambda nome_arquivo, modelo, resultados_gerais, df_planilha, df_infos, df_original, df_outliers, resultados_vars, df_calc_obs: ( + exportar_modelo_completo_avse( + nome_arquivo, modelo, resultados_gerais, df_planilha, df_infos, df_original, + df_outliers, resultados_vars, df_calc_obs + ) + ), + inputs=[ + nome_arquivo_modelo, modelo_state, resultado_geral, dados_state, tabela_cabecalhos, + tabela_original, df_out, resultado_coef, calc_obs + ], + outputs=[saida_salvar_modelo] + ) + # Atualizar visibilidade do status após clique no botão + botao_salvar_modelo.click( + lambda: gr.update(visible=True), + inputs=[], + outputs=[saida_salvar_modelo] + ) + + # 3.7. Botão para conectar à função de carregamento do modelo (VISUALIZAR MODELO) + botao_carregar_modelo.click( + carregar_modelo, + inputs=[modelo_input], + outputs=[ + status_carregamento, # Mensagem de status aparece logo abaixo da caixa de upload + modelo_state, # Estado para armazenar o modelo carregado + resultado_geral_carregado, + tabela_planilha_carregada, tabela_original_carregada, tabela_outliers_carregada, + tabela_resultados_vars_carregada, tabela_calc_obs_carregada, tab_infos_carregada + ] + ) + + # 3.8. Botão para conectar à função de realizar previsões (REALIZAR PREVISÃO) + botao_previsao.click( + realizar_previsoes, + inputs=[modelo_state, tab_infos_carregada], + outputs=[tabela_previsao, previsao_saida, tabela_previsao_ajustada, previsao_ajustada_saida] + ) + + return locals() + + +### Pontos de máximo e mínimo +### Colunas com Coordenadas (ter a possibilidade de dizer se há ou não coordenadas para criar uma dispesão espacial) +### Endereços +### Colunas que possuem valor 0, não podem ser utilizados na ln ou inversa (mensagem de erro?) +### Micronumerosidade +### Média, mediana e moda +### Problema no ordenamento dos dataframes +### Implementar avaliação em massa +### Implementar relatório em word ### Conectar a elaboração do modelo com o carregamento \ No newline at end of file