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 from io import BytesIO 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() # Verificar se dados_out é None e definir um valor padrão, como uma string vazia if dados_out is None: dados_out = "" # 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 # Verificar se dados_out é None e definir um valor padrão, como uma string vazia if dados_out is None: dados_out = "" 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 # Verificar se dados_out é None e definir um valor padrão, como uma string vazia if dados_out is None: dados_out = "" # 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(df_planilha, df_infos, df_original, df_escalado, df_outliers, resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans): try: # Cria um buffer em memória buffer = BytesIO() # Cria o Excel no buffer with pd.ExcelWriter(buffer, engine='xlsxwriter') as writer: 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_outliers.to_excel(writer, sheet_name='Outliers', index=False) # Processar resultados_gerais 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_vars.to_excel(writer, sheet_name='Resultados Variáveis', index=False) df_calc_obs.to_excel(writer, sheet_name='Calc x Obs', index=False) # Move o cursor para o início do buffer buffer.seek(0) # Retorna o conteúdo como arquivo Gradio return buffer.getvalue() 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] ) # Botão para conectar à função de exportar para o Excel 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( df_planilha, df_infos, df_original, df_escalado, df_out, resultados_gerais, resultados_vars, df_calc_obs, df_calc_obs_trans ) ), inputs=[ dados_state, tabela_cabecalhos, tabela_original, tabela_escalada, df_out, resultado_geral, resultado_coef, calc_obs, calc_obs_trans ], 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