Contexto: A escoliose idiopática do adolescente pode apresentar resposta clínica variável a intervenções conservadoras. Objetivo: Explorar associações entre características basais e evolução radiográfica em 6 meses, utilizando abordagem estatística otimizada. Métodos: Estudo observacional com análise exploratória e inferencial. Implementamos três tipos de modelos: (1) modelo linear único para o desfecho contínuo \(\Delta\) (mudança na maior curva), (2) modelos logísticos para desfechos binários de melhora clínica (\(\Delta \le -5°\)) e progressão clínica (\(\Delta \ge +5°\)), e (3) modelos CART para identificação de perfis de risco. Realizamos diagnósticos completos de adequação para todos os modelos, reportando coeficientes/odds ratios com IC95%.
0.1 Como reproduzir
O arquivo de dados é lido de data/dataset_escoliose_01.xlsx (aba dados).
As análises dependem de pacotes R listados no chunk de setup; caso algum pacote esteja ausente, o documento interrompe a execução com uma mensagem explícita.
0.2 Objetivo e delineamento
Este é um relatório exploratório e inferencial: o objetivo é estimar associações ajustadas entre variáveis basais e a evolução radiográfica em 6 meses.
0.3 Estrutura dos Modelos
A análise utiliza uma abordagem estatística otimizada com três tipos de modelos complementares:
0.3.1 Modelo Linear (Desfecho Contínuo)
Objetivo: Modelar diretamente o \(\Delta\) (mudança na maior curva em graus)
Interpretação: Coeficientes negativos indicam associação com melhora, positivos com piora
Vantagem: Aproveita toda a informação contínua do desfecho, sem perda por dicotomização
0.3.2 Modelos para Desfechos Binários
Melhora Clínica (\(\Delta \le -5°\)): Modelos logístico e CART
Progressão Clínica (\(\Delta \ge +5°\)): Modelos logístico e CART
Objetivo: Identificar fatores associados aos desfechos clinicamente relevantes e perfis de risco
0.4 Definições de desfecho
Desfecho contínuo:\(\Delta\) = (maior curva em 6 meses − maior curva baseline), em graus. Valores negativos indicam melhora, positivos indicam piora.
Melhora clínica:\(\Delta \le -5°\) (redução de pelo menos 5 graus na maior curva).
Progressão clínica:\(\Delta \ge +5°\) (aumento de pelo menos 5 graus na maior curva).
0.5 Plano de análise estatística
Descrição da amostra: estatísticas descritivas das variáveis preditoras e desfechos.
Inferência (associações ajustadas):
Modelo linear único para \(\Delta\) (coeficientes com IC95%, interpretação direcionada).
Modelos logísticos específicos para melhora e progressão (odds ratios com IC95%).
Modelos CART para ambos os desfechos binários (regras interpretáveis e perfis de risco).
Adequação dos modelos: diagnósticos completos de pressupostos para todos os modelos (normalidade, heteroscedasticidade, colinearidade, outliers, adequação do ajuste).
Forma funcional: relações aproximadamente lineares entre preditores contínuos e desfechos.
Por se tratar de análise exploratória, os resultados devem ser interpretados com cautela, com ênfase em magnitude/direção e incerteza (IC95%).
Código
# =============================================================================# SETUP E PREPARAÇÃO DOS DADOS# =============================================================================## Carregamento e verificação de pacotes necessáriospkgs <-c("tidyverse", # manipulação de dados e visualização"gtsummary", # tabelas estatísticas"readxl", # leitura de arquivos Excel"janitor", # limpeza de nomes de variáveis"performance", # diagnósticos de modelos"qqplotr", # gráficos Q-Q"PupillometryR"# gráficos violin plot)# Verificar se todos os pacotes estão instaladosmissing_pkgs <- pkgs[!vapply(pkgs, requireNamespace, logical(1), quietly =TRUE)]if (length(missing_pkgs) >0) {stop("Pacotes ausentes: ", paste(missing_pkgs, collapse =", "))}# Carregar pacotesinvisible(lapply(pkgs, library, character.only =TRUE))# Configura<U+00E7><U+00F5>es de reprodutibilidade e temaset.seed(123)theme_set(theme_minimal())## Leitura dos dadosdf_raw <- readxl::read_excel("data/dataset_escoliose_01.xlsx",sheet ="dados",na =""# tratar células vazias como NA)## Limpeza dos nomes das variáveis (snake_case)df <- df_raw |> janitor::clean_names()# =============================================================================# CRIAÇÃO DE VARIÁVEIS DERIVADAS# =============================================================================## Classificação do escoliômetro: identificar regi<U+00F5>es com deformidade > 10°# Esta variável indica onde a gibosidade é mais pronunciadadf <- df |>mutate(escoliometro_maior_10_graus =case_when(# Múltiplas regi<U+00F5>es afetadas (escoliometro_cervical >10) & (escoliometro_torarica >10) & (escoliometro_lombar >10) ~"cervical_toracica_lombar", (escoliometro_cervical >10) & (escoliometro_torarica >10) ~"cervical_toracica", (escoliometro_cervical >10) & (escoliometro_lombar >10) ~"cervical_lombar", (escoliometro_torarica >10) & (escoliometro_lombar >10) ~"toracica_lombar",# Região única afetada (escoliometro_cervical >10) ~"cervical", (escoliometro_torarica >10) ~"toracica", (escoliometro_lombar >10) ~"lombar",# Nenhuma região > 10° (referência)TRUE~"normal" ) )# Variáveis categóricas não são mais utilizadas nos modelos finais# (mantendo apenas as variáveis numéricas conforme plano de análise)## Cálculo do Índice de Massa Corporal (IMC)df <- df |>mutate(imc = peso / (altura)^2) # altura deve estar em metros# =============================================================================# DEFINIÇÃO DOS DESFECHOS PRIMÁRIOS# =============================================================================## Identificação da maior curva no baseline# Compara as três regi<U+00F5>es e identifica o maior ângulo de Cobb inicialdf <- df |>mutate(# Maior curva entre todas as regi<U+00F5>es medidascobb_inicial_maior =pmax( cobb_toracico_proximal, cobb_toracica, cobb_lombar,na.rm =TRUE ),# Identificar qual região apresenta a maior curva (para referência)regiao_cobb_inicial =case_when( cobb_inicial_maior == cobb_toracico_proximal ~"toracico_proximal", cobb_inicial_maior == cobb_toracica ~"toracica", cobb_inicial_maior == cobb_lombar ~"lombar",TRUE~NA_character_ ) )# =============================================================================# CRIAÇÃO DAS VARIÁVEIS DE DESFECHO# =============================================================================## Variáveis de desfecho para os dois modelos principaisdf <- df |># DESFECHO CONTÍNUO: Mudan<U+00E7>a na maior curva (em graus)# Valores negativos = melhora, valores positivos = pioramutate(delta = maior_curva_6_meses - cobb_inicial_maior) |># MODELO 1: MELHORA CLÍNICA (delta ≤ -5°)mutate(delta_cat =if_else(delta <=-5, 1, 0),delta_cat_f =factor( delta_cat,levels =c(0, 1),labels =c("Sem melhora (MCID)", "Melhora (MCID)") ) ) |># MODELO 2: PROGRESS<U+00C3>O CLÍNICA (delta ≥ +5°)mutate(delta_progression =if_else(delta >=5, 1, 0),delta_progression_f =factor( delta_progression,levels =c(0, 1),labels =c("Não progrediu", "Progrediu") ) )# Conversão de variáveis para tipos adequados nos modelosdf <- df |>mutate(# Variáveis numéricasidade =as.double(idade),# Variáveis categóricas (fatores com níveis específicos)lenke =as.factor(lenke),risser =as.factor(risser),sexo =as.factor(sexo),flexibilidade =as.factor(flexibilidade),# Escoliômetro: definir "normal" como categoria de referênciaescoliometro_maior_10_graus = forcats::fct_relevel(as.factor(escoliometro_maior_10_graus),"normal" ) )
0.6 Dados faltantes e tamanho amostral
Código
# =============================================================================# ANÁLISE DE DADOS FALTANTES# =============================================================================# Calcular quantidade e percentual de dados faltantes por variávelmissing_tbl <- df |>summarise(across(everything(), ~sum(is.na(.)))) |>pivot_longer(everything(),names_to ="variavel",values_to ="n_missing" ) |>mutate(pct_missing = n_missing /nrow(df)) |>arrange(desc(pct_missing)) # ordenar por maior percentual faltante# Exibir tabela de dados faltantes (formato percentual)missing_tbl |>mutate(pct_missing = scales::percent(pct_missing, accuracy =0.1)) |>print(n =50)
# Tratamento de dados faltantes# Remover observa<U+00E7><U+00F5>es com flexibilidade faltante (variável key do modelo)df <- df |>drop_na(flexibilidade)
Observação: os modelos abaixo usam análise por caso completo (listwise deletion), que é o comportamento padrão do glm()/lm() quando há dados faltantes nas variáveis do modelo.
0.7 Codificação de variáveis categóricas (referências)
As variáveis categóricas entram como fatores. A categoria de referência (baseline) é o primeiro nível do fator (conforme levels()), a menos que seja explicitamente reordenado.
Código
# Lista de variáveis categóricas utilizadas nos modelos finaiscategoricas <-c("sexo","lenke","risser","flexibilidade","escoliometro_maior_10_graus")map_dfr(categoricas, function(v) {tibble(variavel = v,niveis =paste(levels(df[[v]]), collapse =" | ") )}) |>print(n =Inf)
Os coeficientes do modelo linear representam a mudança esperada no \(\Delta\) (em graus) para cada unidade de aumento na variável preditora:
Coeficientes negativos: indicam associação com melhora clínica (redução na curvatura)
Coeficientes positivos: indicam associação com progressão clínica (aumento na curvatura)
Magnitude: quantifica o efeito em graus de mudança na maior curva
Intervalos de confiança: estimam a precisão e significância estatística dos efeitos
Código
# =============================================================================# MODELO LINEAR - DESFECHO CONTÍNUO (DELTA)# =============================================================================# Modelo único para o desfecho contínuo (delta em graus)# Interpretação direta: coeficientes negativos = melhora, positivos = piora# Utiliza toda a informação contínua sem perda por dicotomizaçãomodel_delta <-lm( delta ~# Variáveis numéricas idade +# idade do paciente imc +# índice de massa corporal cifose_toracica +# ângulo da cifose torácica lordose_lombar +# ângulo da lordose lombar correcao_colete +# percentual de correção com colete# Variáveis categóricas sexo +# sexo do paciente lenke +# classificação de Lenke (numérica como factor) risser +# sinal de Risser (numérica como factor) flexibilidade +# flexibilidade da curva escoliometro_maior_10_graus, # região com gibosidade > 10°data = df)
Verificação de colinearidade entre as variáveis independentes (Variance Inflation Factor). Valores maiores que 10 indicam colinearidade entre as variáveis.
Análise da melhora clínica, definida como redução ≥ 5 graus na maior curva (\(\Delta \le -5°\)).
2.2.1 Modelo Logístico
Estima associações ajustadas com a chance de melhora clínica, definida como \(\Delta \le -5°\).
Código
# =============================================================================# MODELO LOGÍSTICO - MELHORA CLÍNICA# =============================================================================# Modelo para chance de melhora clínica (delta ≤ -5°)# Utiliza as mesmas variáveis do modelo linearmodel_melhora <-glm( delta_cat ~# Variáveis numéricas idade +# idade do paciente imc +# índice de massa corporal cifose_toracica +# ângulo da cifose torácica lordose_lombar +# ângulo da lordose lombar correcao_colete +# percentual de correção com colete# Variáveis categóricas sexo +# sexo do paciente lenke +# classificação de Lenke risser +# sinal de Risser flexibilidade +# flexibilidade da curva escoliometro_maior_10_graus, # região com gibosidade > 10°data = df,family ="binomial"# distribuição binomial para regressão logística)
2.2.1.1 Parâmetros do modelo
Parâmetros do modelo de regressão logística. Resposta em Odds Ratio.
Teste de Hosmer-Lemeshow para avaliar a qualidade do ajuste de modelos binomiais. P-valor < 0.05 indica que o modelo não ajusta bem os dados.
Código
# Teste de Hosmer-Lemeshow pode falhar com poucos eventos de melhoratryCatch( { performance::performance_hosmer(model_melhora) },error =function(e) {cat("Aviso: Teste de Hosmer-Lemeshow não pode ser executado.\n")cat("Possível causa: poucos eventos de melhora ou falta de variação nas predi<U+00E7><U+00F5>es.\n")cat("Erro:", e$message, "\n") })
# Hosmer-Lemeshow Goodness-of-Fit Test
Chi-squared: 18.657
df: 8
p-value: 0.017
2.2.1.6 Colinearidade
Verificação de colinearidade entre as variáveis independentes (Variance Inflation Factor). Valores maiores que 10 indicam colinearidade entre as variáveis.
Visualização da incerteza nas estimativas de desempenho:
Código
cart_res |>collect_metrics(summarize =FALSE) |>ggplot(aes(x = .metric, y = .estimate, fill = .metric)) +geom_boxplot(alpha =0.7, show.legend =FALSE) +geom_jitter(width =0.1, alpha =0.3, size =1) +scale_fill_brewer(palette ="Set2") +labs(title ="Distribution of metrics by fold (CV repeated)",subtitle ="10-fold CV x 5 repetitions = 50 estimates per metric",x ="Metric",y ="Value" ) +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"),axis.text.x =element_text(angle =0) )
2.2.2.8 Matriz de confusão agregada (normalizada)
Código
# Predicoes agregadas de todos os foldspreds_all <- cart_res |>collect_predictions()# Matriz de confusaoconf_mat_data <- preds_all |>conf_mat(truth = delta_cat_cart, estimate = .pred_class)# Normalizar por linha (proporcao por classe verdadeira)conf_mat_tbl <- conf_mat_data$table |>as.data.frame() |>group_by(Truth) |>mutate(prop = Freq /sum(Freq),label = scales::percent(prop, accuracy =0.1) ) |>ungroup()# Visualizacao heatmap normalizadoggplot(conf_mat_tbl, aes(x = Prediction, y = Truth, fill = prop)) +geom_tile(color ="white", linewidth =1) +geom_text(aes(label = label), size =5, fontface ="bold") +scale_fill_gradient(low ="white",high ="steelblue",labels = scales::percent,name ="Proporcao" ) +scale_y_discrete(limits = rev) +labs(title ="Confucion Matrix",subtitle ="Proportion by true class (rows sum to 100%)",x ="Prediction",y ="True" ) +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"),panel.grid =element_blank() )
2.2.2.9 Árvore final
Ajustamos o modelo final no dataset completo para visualização e interpretação:
Código
cart_fit_full <-fit(cart_final_wf, data = df_cart)cart_rpart <-extract_fit_engine(cart_fit_full)# Plot da arvore com rpart.plotrpart.plot( cart_rpart,type =4,extra =104,under =TRUE,fallen.leaves =TRUE,roundint =FALSE,box.palette ="BuGn",shadow.col ="gray",main ="Decision Tree for MCID (delta <= -5 degrees)")
2.2.2.10 Importância de variáveis
Código
# Grafico de importancia com vipcart_fit_full |>extract_fit_parsnip() |>vip(num_features =15,geom ="col",aesthetics =list(fill ="steelblue", alpha =0.8) ) +labs(title ="Variable Importance (CART)",subtitle ="Based on impurity reduction (Gini)",x ="Importance",y ="Variable" ) +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"))
2.2.2.11 Análise de estabilidade (Bootstrap)
Código
# Função para extrair variáveis de split principais de uma árvore (únicas por árvore)extract_main_splits <-function(rpart_model, max_levels =3) { frame <- rpart_model$frame# Filtrar apenas nós internos (não folhas) internal_nodes <- frame[frame$var !="<leaf>", ]# Extrair apenas as variáveis (não os valores de corte)if (nrow(internal_nodes) >0) {# Limitar aos primeiros níveis (mais estáveis) rownames_numeric <-as.numeric(rownames(internal_nodes)) main_splits <- internal_nodes[rownames_numeric <= (2^max_levels -1), ]# Retornar apenas variáveis ÚNICAS por árvorereturn(unique(main_splits$var)) } else {return(character(0)) }}# Bootstrap para verificar consistência dos splitsset.seed(123)n_bootstrap <-100bootstrap_splits <-list()for (i in1:n_bootstrap) {# Amostra bootstrap boot_sample <- df_cart[sample(nrow(df_cart), replace =TRUE), ]# Ajustar modelo com mesmos hiperparâmetros boot_model <-decision_tree(cost_complexity = best_params$cost_complexity,tree_depth = best_params$tree_depth,min_n = best_params$min_n ) |>set_engine("rpart") |>set_mode("classification") |>fit(delta_cat_cart ~ ., data = boot_sample)# Extrair splits principais boot_rpart <-extract_fit_engine(boot_model) bootstrap_splits[[i]] <-extract_main_splits(boot_rpart)}# Análise de estabilidademain_vars_stability <-table(unlist(bootstrap_splits))stability_summary <-sort(main_vars_stability / n_bootstrap, decreasing =TRUE)cat("Estabilidade das variáveis nos primeiros splits (% de árvores que usam cada variável):\n")
Estabilidade das variáveis nos primeiros splits (% de árvores que usam cada variável):
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=0,2,3,4 E lenke=2,3,4,6 E sexo=feminino
Grupo 1
nao_melhora
66
3.0%
correcao_colete< 49.42 E correcao_colete< 29.45
Grupo 6
nao_melhora
11
27.3%
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=1 E idade< 11.5
Grupo 4
nao_melhora
10
0.0%
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=0,2,3,4 E lenke=1,5 E lordose_lombar< 45
Grupo 7
nao_melhora
9
44.4%
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=1 E idade>=11.5 E cifose_toracica< 11
Grupo 9
nao_melhora
9
22.2%
correcao_colete>=49.42 E correcao_colete< 61.72 E escoliometro_maior_10_graus=toracica_lombar
Grupo 10
nao_melhora
9
11.1%
correcao_colete>=49.42 E correcao_colete< 61.72 E escoliometro_maior_10_graus=normal,lombar,toracica E imc< 15.84 E cifose_toracica< 19.5
Grupo 13
melhora
115
96.5%
correcao_colete>=49.42 E correcao_colete>=61.72
Grupo 12
melhora
99
80.8%
correcao_colete>=49.42 E correcao_colete< 61.72 E escoliometro_maior_10_graus=normal,lombar,toracica E imc>=15.84
Grupo 5
melhora
33
72.7%
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=0,2,3,4 E lenke=1,5 E lordose_lombar>=45
Grupo 8
melhora
26
84.6%
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=1 E idade>=11.5 E cifose_toracica>=11
Grupo 3
melhora
25
64.0%
correcao_colete< 49.42 E correcao_colete>=29.45 E risser=0,2,3,4 E lenke=2,3,4,6 E sexo=masculino
Grupo 11
melhora
14
64.3%
correcao_colete>=49.42 E correcao_colete< 61.72 E escoliometro_maior_10_graus=normal,lombar,toracica E imc< 15.84 E cifose_toracica>=19.5
2.2.2.13 Curva ROC
Código
# Curva ROC a partir das predicoes CVroc_data <- preds_all |>roc_curve(truth = delta_cat_cart, .pred_melhora, event_level ="second")# AUC para o tituloauc_val <- preds_all |>roc_auc(truth = delta_cat_cart, .pred_melhora, event_level ="second") |>pull(.estimate)ggplot(roc_data, aes(x =1- specificity, y = sensitivity)) +geom_path(linewidth =1.2, color ="steelblue") +geom_abline(linetype ="dashed", color ="gray50") +geom_ribbon(aes(ymin =0, ymax = sensitivity),alpha =0.1,fill ="steelblue" ) +annotate("text",x =0.6,y =0.3,label =paste0("AUC = ", round(auc_val, 3)),size =5,fontface ="bold" ) +labs(title ="ROC Curve (CV)",subtitle ="Based on aggregated predictions from all folds",x ="1 - Specificity (False Positive Rate)",y ="Sensitivity (True Positive Rate)" ) +coord_equal() +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"))
2.3 Desfecho Binário: Progressão Clínica
Análise da progressão clínica, definida como piora ≥ 5 graus na maior curva (\(\Delta \ge +5°\)).
2.3.1 Modelo Logístico
Estima associações ajustadas com a chance de progressão clínica, definida como \(\Delta \ge +5°\).
Código
# =============================================================================# MODELO LOGÍSTICO - PROGRESS<U+00C3>O CLÍNICA# =============================================================================# Modelo para chance de progressão clínica (delta ≥ +5°)# Utiliza as mesmas variáveis dos outros modelosmodel_progressao <-glm( delta_progression ~# Variáveis numéricas idade +# idade do paciente imc +# índice de massa corporal cifose_toracica +# ângulo da cifose torácica lordose_lombar +# ângulo da lordose lombar correcao_colete +# percentual de correção com colete# Variáveis categóricas sexo +# sexo do paciente lenke +# classificação de Lenke risser +# sinal de Risser flexibilidade +# flexibilidade da curva escoliometro_maior_10_graus, # região com gibosidade > 10°data = df,family ="binomial"# distribuição binomial para regressão logística)
2.3.1.1 Parâmetros do modelo
Parâmetros do modelo de regressão logística. Resposta em Odds Ratio.
Teste de Hosmer-Lemeshow para avaliar a qualidade do ajuste de modelos binomiais. P-valor < 0.05 indica que o modelo não ajusta bem os dados.
Código
# Teste de Hosmer-Lemeshow pode falhar com poucos eventos de progressãotryCatch( { performance::performance_hosmer(model_progressao) },error =function(e) {cat("Aviso: Teste de Hosmer-Lemeshow não pode ser executado.\n")cat("Possível causa: poucos eventos de progressão ou falta de variação nas predições.\n")cat("Erro:", e$message, "\n") })
# Hosmer-Lemeshow Goodness-of-Fit Test
Chi-squared: 1.030
df: 8
p-value: 0.998
2.3.1.6 Colinearidade
Verificação de colinearidade entre as variáveis independentes (Variance Inflation Factor). Valores maiores que 10 indicam colinearidade entre as variáveis.
Variabilidade das métricas entre os folds de validação cruzada.
Código
cart_res_progressao |>collect_metrics(summarize =FALSE) |>ggplot(aes(x = .metric, y = .estimate, fill = .metric)) +geom_boxplot(alpha =0.7, show.legend =FALSE) +geom_jitter(width =0.1, alpha =0.3, size =1) +scale_fill_brewer(palette ="Set2") +labs(title ="Distribution of metrics by fold (CV repeated)",subtitle ="10-fold CV x 5 repetitions = 50 estimates per metric",x ="Metric",y ="Value" ) +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"),axis.text.x =element_text(angle =0) )
2.3.2.8 Matriz de confusão agregada (normalizada)
Código
# Matriz de confusão agregadapreds_progressao <- cart_res_progressao |>collect_predictions()# Predicoes agregadas de todos os folds (mantendo fonte de dados do contexto do documento)preds_all_progressao <- cart_res_progressao |>collect_predictions()# Matriz de confusaoconf_mat_data_progressao <- preds_all_progressao |>conf_mat(truth = delta_progression_cart, estimate = .pred_class)# Normalizar por linha (proporcao por classe verdadeira)conf_mat_tbl_progressao <- conf_mat_data_progressao$table |>as.data.frame() |> dplyr::group_by(Truth) |> dplyr::mutate(prop = Freq /sum(Freq),label = scales::percent(prop, accuracy =0.1) ) |> dplyr::ungroup()# Visualizacao heatmap normalizadoggplot(conf_mat_tbl_progressao, aes(x = Prediction, y = Truth, fill = prop)) +geom_tile(color ="white", linewidth =1) +geom_text(aes(label = label), size =5, fontface ="bold") +scale_fill_gradient(low ="white",high ="steelblue",labels = scales::percent,name ="Proporcao" ) +scale_y_discrete(limits = rev) +labs(title ="Matriz de Confusão - Progressão (Normalizada por linha)",subtitle ="Agregada de todos os folds de validação cruzada" ) +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"),panel.grid =element_blank() )
2.3.2.9 Árvore final
Ajustamos o modelo final no dataset completo para visualização e interpretação:
Código
cart_fit_full_progressao <-fit(cart_final_wf_progressao, data = df_cart_progressao)cart_rpart_progressao <-extract_fit_engine(cart_fit_full_progressao)# Plot da arvore com rpart.plotrpart.plot( cart_rpart_progressao,type =4,extra =104,under =TRUE,fallen.leaves =TRUE,roundint =FALSE,box.palette ="BuGn",shadow.col ="gray",main ="Decision Tree for Progression (delta >= +5 degrees)")
2.3.2.10 Importância de variáveis
Código
# Verificar se a árvore tem splits antes de calcular importânciatryCatch( {# Verificar se há splits na árvore frame_check <- cart_rpart_progressao$frame internal_nodes <-sum(frame_check$var !="<leaf>")if (internal_nodes >0) {# Grafico de importancia com vip cart_fit_full_progressao |>extract_fit_parsnip() |>vip(num_features =min(15, internal_nodes),geom ="col",aesthetics =list(fill ="darkred", alpha =0.8) ) +labs(title ="Variable Importance (CART - Progressão)",subtitle ="Based on impurity reduction (Gini)",x ="Importance",y ="Variable" ) +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold")) } else {cat("Aviso: Árvore de progressão não possui splits suficientes para análise de importância.\n")cat("Possível causa: poucos eventos de progressão ou parâmetros muito restritivos.\n")# Plot informativoggplot() +annotate("text",x =0.5, y =0.5,label ="Árvore sem splits suficientes\npara análise de importância",size =6, hjust =0.5 ) +labs(title ="Variable Importance (CART - Progressão)",subtitle ="Modelo muito simples para análise de importância" ) +theme_void() } },error =function(e) {cat("Erro na análise de importância:", e$message, "\n")cat("Possível causa: modelo CART muito simples ou dados insuficientes.\n") })
2.3.2.11 Análise de estabilidade (Bootstrap)
Código
# Bootstrap para verificar consistência dos splitsset.seed(123)n_bootstrap <-100bootstrap_splits_progressao <-list()for (i in1:n_bootstrap) {# Amostra bootstrap boot_sample <- df_cart_progressao[sample(nrow(df_cart_progressao), replace =TRUE), ]# Ajustar modelo com mesmos hiperparâmetros boot_model <-decision_tree(cost_complexity = best_params_progressao$cost_complexity,tree_depth = best_params_progressao$tree_depth,min_n = best_params_progressao$min_n ) |>set_engine("rpart") |>set_mode("classification") |>fit(delta_progression_cart ~ ., data = boot_sample)# Extrair splits principais boot_rpart <-extract_fit_engine(boot_model) bootstrap_splits_progressao[[i]] <-extract_main_splits(boot_rpart)}# Análise de estabilidademain_vars_stability_progressao <-table(unlist(bootstrap_splits_progressao))stability_summary_progressao <-sort(main_vars_stability_progressao / n_bootstrap, decreasing =TRUE)cat("Estabilidade das variáveis nos primeiros splits (% de árvores que usam cada variável):\n")
Estabilidade das variáveis nos primeiros splits (% de árvores que usam cada variável):