6  Processo de estimação

Após a seleção dos modelos e o pré-processamento, a presente seção estima as duas especificações da AFC na subamostra de calibração. A estimação traduz as relações previstas nos modelos teóricos em parâmetros empíricos, sob as condições estabelecidas nas etapas anteriores: tratamento contínuo dos itens, estimador MLR, procedimento FIML e identificação pela fixação unitária da variância dos fatores (Jackson et al., 2009; Nye, 2022).

Os 1.243 participantes da amostra de análise foram divididos aleatoriamente em duas parcelas: 621 para calibração e 622 para validação. A partição foi estratificada pela presença de respostas ausentes nos 24 indicadores, distribuindo os 60 participantes com alguma omissão de forma equilibrada entre os dois grupos. A subamostra de calibração é empregada para estimar os modelos e examinar seus diagnósticos, ao passo que a subamostra de validação é reservada à confirmação empírica da reespecificação selecionada.

6.1 Fundamentação da estimação

Os itens foram tratados como variáveis contínuas, conforme a fundamentação desenvolvida na Seção 2.4. De acordo com Rhemtulla et al. (2012), o estimador MLR exibe desempenho adequado em configurações com cinco ou mais categorias ordinais, em especial quando os limiares de resposta distribuem-se de maneira aproximadamente simétrica. Robitzsch (2020) e Robitzsch (2022) demonstram que as formulações contínua e ordinal definem alvos paramétricos distintos. Com cinco categorias e uma estrutura fatorial previamente fundamentada, adota-se a formulação contínua como aproximação de mensuração no presente estudo.

O método MLR foi selecionado por combinar a ML com erros-padrão robustos e estatísticas de teste corrigidas por escala. Essa combinação ajusta-se com precisão às distribuições observadas no pré-processamento e atende às recomendações metodológicas para dados sob violação de normalidade multivariada (Brown, 2015; Kline, 2023; Rogers, 2024).

O volume residual de respostas ausentes foi tratado por meio do FIML. O procedimento aproveita toda a informação observada de cada respondente e preserva os 621 casos da subamostra de calibração. A opção segue as diretrizes da literatura para tratamento de dados omissos sob o método de ML e suas variantes robustas (Brown, 2023; Kline, 2023).

A escala dos fatores latentes foi definida fixando-se suas variâncias em 1. Essa estratégia de identificação permite estimar livremente todas as cargas fatoriais e assegura a mesma métrica nos dois modelos alternativos. Ambas as especificações foram estimadas sobre a mesma subamostra de calibração e sob condições analíticas idênticas. A comparação empírica concentra-se, assim, nas três relações cruzadas adicionais postuladas no modelo H1(Jackson et al., 2009; Nye, 2022).

Finalidade

Dividir a base de dados em duas subamostras (calibração e validação), balanceando a presença de respostas incompletas em ambas. A subamostra de calibração viabiliza a estimação e os diagnósticos dos modelos; a de validação destina-se à confirmação da especificação selecionada.

Preparação

Utiliza-se Data/AnalysisData/whoqol_analysis.csv, gerado na etapa de pré-processamento (Seção 5), conforme a estrutura descrita na Seção 3. A partição abrange os 24 indicadores (Q3–Q26), antes de qualquer exclusão de itens.

Procedimento

O chunk abaixo lê a base, preserva os identificadores como texto e ordena os registros por ID. A variável missing_flag recebe 0 para quem respondeu aos 24 indicadores e 1 para quem apresenta omissões nesse conjunto. Esses dois estratos orientam o sorteio aleatório.

Em cada estrato, a semente fixa a partição de metade dos registros para a subamostra de calibração. Quando o estrato tem tamanho ímpar, o registro excedente é alocado na validação. O vetor sample_group identifica a subamostra: 0 para calibração e 1 para validação. Essa variável também pode identificar grupos em análises de invariança, distinguindo-se de missing_flag, restrita à estratificação de ausências.

O objeto cv_data conserva todos os participantes e as duas flags. A partição é salva em Data/AnalysisData/whoqol_cv.csv; nas execuções subsequentes, o bloco lê esse arquivo e verifica sua integridade com os dados de entrada. Assim, as seções posteriores recuperam exatamente a mesma partição, de forma reprodutível e independente da sessão.

Execução e resultado

Execute o bloco abaixo antes da Tabela 6.1. Ele mantém a base completa em cv_data e extrai calibration_data para a estimação dessa seção. A subamostra de validação permanece identificada por sample_group == 1 no arquivo gravado e será recuperada na etapa oportuna, preservando-se a integridade do banco original.

Código
# Read and order the complete analysis sample before splitting it.
cv_data <- readr::read_csv2(
  here::here("Data", "AnalysisData", "whoqol_analysis.csv"),
  na = c("", "NA"),
  col_types = readr::cols(ID = readr::col_character()),
  show_col_types = FALSE
)
cv_data <- cv_data[order(cv_data$ID), ]

# Identify incomplete responses using all 24 planned indicators.
cv_data$missing_flag <- as.integer(
  rowSums(is.na(cv_data[paste0("Q", 3:26)])) > 0L
)
stopifnot(!anyNA(cv_data$ID), !anyDuplicated(cv_data$ID))

# Reuse the saved partition when its input data remain unchanged.
cv_file <- here::here(
  "Data", "AnalysisData", "whoqol_cv.csv"
)
if (file.exists(cv_file)) {
  saved_cv <- readr::read_csv2(
    cv_file, na = c("", "NA"),
    col_types = readr::cols(
      ID = readr::col_character(),
      missing_flag = readr::col_integer(),
      sample_group = readr::col_integer()
    ),
    show_col_types = FALSE
  )
  stopifnot(
    identical(
      as.data.frame(saved_cv[names(cv_data)]), as.data.frame(cv_data)
    ),
    all(saved_cv$sample_group %in% c(0, 1))
  )
  cv_data$sample_group <- as.integer(saved_cv$sample_group)
} else {
  # Assign calibration (0) and validation (1) within each missingness stratum.
  set.seed(SEED)
  cv_data$sample_group <- 1L
  for (stratum in 0:1) {
    stratum_rows <- which(cv_data$missing_flag == stratum)
    calibration_rows <- stratum_rows[sample.int(
      length(stratum_rows), size = floor(length(stratum_rows) / 2)
    )]
    cv_data$sample_group[calibration_rows] <- 0L
  }

  # Preserve the original data and save the partition for later sections.
  dir.create(dirname(cv_file), recursive = TRUE, showWarnings = FALSE)
  readr::write_excel_csv2(cv_data, cv_file, na = "NA")
}

# Check stratum allocation and select the calibration sample.
stopifnot(
  all(table(cv_data$missing_flag, cv_data$sample_group)[, "0"] ==
    floor(table(cv_data$missing_flag) / 2))
)
calibration_data <- cv_data[cv_data$sample_group == 0L, ]

6.2 Parâmetros estimados

Antes da avaliação do ajuste, a Tabela 6.1 apresenta os parâmetros fatoriais estimados. Para cada relação entre fator e item, a tabela sintetiza a carga não padronizada, o erro-padrão robusto, a estatística \(z\) de teste e a carga padronizada, conforme recomendado por Jackson et al. (2009) e Nye (2022).

As cargas não padronizadas reportam a métrica original da escala. Por sua vez, as cargas padronizadas viabilizam a comparação direta da magnitude das relações em cada especificação, enquanto os erros-padrão asseguram inferência estatística corrigida para desvios de normalidade pelo método MLR.

A tabela consolida ambos os modelos, evidenciando as três relações adicionais de H1 ao lado das cargas primárias. A avaliação dessas cargas conjuga os parâmetros estimados aos índices de ajuste global e local examinados nas seções subsequentes.

Tabela 6.1: Parâmetros fatoriais estimados nos modelos H0 e H1.
Modelo Relação Estimativa EP z Padronizada
H0 psycho =~ Q5 0.523 0.033 15.774 0.639
H0 psycho =~ Q6 0.591 0.042 13.918 0.597
H0 psycho =~ Q7 0.561 0.031 18.139 0.653
H0 psycho =~ Q11 0.639 0.040 16.047 0.637
H0 psycho =~ Q19 0.764 0.033 23.296 0.798
H0 psycho =~ Q26 0.559 0.041 13.789 0.585
H0 physical =~ Q3 0.435 0.043 10.079 0.459
H0 physical =~ Q4 0.435 0.046 9.528 0.429
H0 physical =~ Q10 0.681 0.033 20.662 0.729
H0 physical =~ Q15 0.437 0.042 10.497 0.511
H0 physical =~ Q16 0.585 0.037 15.689 0.568
H0 physical =~ Q17 0.838 0.027 30.776 0.907
H0 physical =~ Q18 0.830 0.029 28.401 0.851
H0 social =~ Q20 0.711 0.033 21.654 0.806
H0 social =~ Q21 0.485 0.044 10.996 0.481
H0 social =~ Q22 0.637 0.035 18.180 0.727
H0 environment =~ Q8 0.448 0.041 10.919 0.455
H0 environment =~ Q9 0.453 0.037 12.206 0.529
H0 environment =~ Q12 0.751 0.041 18.249 0.688
H0 environment =~ Q13 0.508 0.038 13.259 0.624
H0 environment =~ Q14 0.631 0.039 16.006 0.667
H0 environment =~ Q23 0.525 0.041 12.939 0.535
H0 environment =~ Q24 0.455 0.042 10.814 0.478
H0 environment =~ Q25 0.491 0.043 11.407 0.504
H1 psycho =~ Q5 0.523 0.033 15.708 0.639
H1 psycho =~ Q6 0.593 0.043 13.923 0.600
H1 psycho =~ Q7 0.564 0.031 18.087 0.656
H1 psycho =~ Q11 0.640 0.040 16.068 0.639
H1 psycho =~ Q19 0.764 0.033 23.193 0.798
H1 psycho =~ Q26 0.559 0.041 13.779 0.585
H1 psycho =~ Q8 0.190 0.071 2.693 0.193
H1 psycho =~ Q9 0.126 0.056 2.235 0.147
H1 physical =~ Q3 0.427 0.043 9.991 0.451
H1 physical =~ Q4 0.430 0.046 9.432 0.424
H1 physical =~ Q10 0.676 0.033 20.293 0.722
H1 physical =~ Q15 0.253 0.049 5.206 0.296
H1 physical =~ Q16 0.584 0.037 15.638 0.568
H1 physical =~ Q17 0.846 0.027 31.669 0.915
H1 physical =~ Q18 0.834 0.029 29.055 0.855
H1 social =~ Q20 0.712 0.033 21.677 0.806
H1 social =~ Q21 0.485 0.044 10.998 0.480
H1 social =~ Q22 0.636 0.035 18.134 0.726
H1 environment =~ Q8 0.282 0.070 4.027 0.286
H1 environment =~ Q9 0.346 0.060 5.810 0.404
H1 environment =~ Q12 0.762 0.040 18.882 0.698
H1 environment =~ Q13 0.518 0.038 13.527 0.637
H1 environment =~ Q14 0.639 0.039 16.324 0.676
H1 environment =~ Q23 0.522 0.041 12.807 0.532
H1 environment =~ Q24 0.457 0.042 10.795 0.481
H1 environment =~ Q25 0.494 0.044 11.329 0.508
H1 environment =~ Q15 0.301 0.051 5.865 0.352

Finalidade

Estimar os dois modelos na subamostra de calibração e consolidar as cargas fatoriais em tabela única, contendo estimativas não padronizadas, erros-padrão robustos, estatísticas \(z\) e cargas padronizadas.

Preparação

O procedimento utiliza as especificações analyzemodel e h1modelfree, definidas em models.R, e as funções de helpers.R, conforme a organização apresentada no início da Seção 3. O chunk anterior, prepare-cv-partition, lê a base produzida no pré-processamento (Seção 5.2.1) e disponibiliza a subamostra de calibração em calibration_data. Os dois ajustes utilizam esse objeto diretamente. A exportação utiliza a pasta Output/Results/Tables.

Procedimento

O bloco estima H0 e H1 na subamostra de calibração sob condições idênticas. Em seguida, organiza as cargas de cada ajuste e reúne os dois conjuntos em uma tabela unificada, identificando o modelo de origem de cada linha.

A função table_par() recebe cada ajuste e prepara sua tabela de parâmetros. Com lang = "pt", utiliza rótulos em português e mantém a seleção padrão de cargas livres. A rotina auxiliar tidy_par() preserva os parâmetros numéricos completos. O atributo table_data reúne as estimativas para apresentação em tela e exportação, enquanto par_data retém os parâmetros numéricos com precisão completa. Antes de combinar as tabelas, o código verifica se os dados de apresentação estão disponíveis e íntegros. A exportação reproduz os mesmos rótulos, ordem e valores de exibição em formato CSV compatível com o Excel.

Execução e resultado

Após a preparação indicada, execute o bloco completo. Ele gera a tabela apresentada acima, preserva os ajustes fit_analyzemodel e fit_h1modelfree na sessão e salva tbl-estimation-parameters.csv em Output/Results/Tables.

Código
# Estimate H0 using MLR/FIML and unit latent variances.
fit_analyzemodel <- lavaan::cfa(
  model = analyzemodel,
  data = calibration_data,
  estimator = "MLR",
  missing = "fiml",
  std.lv = TRUE
)

# Estimate H1 under the same analytical conditions.
fit_h1modelfree <- lavaan::cfa(
  model = h1modelfree,
  data = calibration_data,
  estimator = "MLR",
  missing = "fiml",
  std.lv = TRUE
)

# Prepare each model's formatted loading table.
parameter_table_h0 <- table_par(fit_analyzemodel, lang = "pt")
parameter_table_h1 <- table_par(fit_h1modelfree, lang = "pt")

# Validate the display data before combining and exporting the tables.
parameter_display_h0 <- attr(parameter_table_h0, "table_data")
parameter_display_h1 <- attr(parameter_table_h1, "table_data")
if (!is.data.frame(parameter_display_h0) ||
    !is.data.frame(parameter_display_h1) ||
    nrow(parameter_display_h0) == 0L || nrow(parameter_display_h1) == 0L) {
  stop("table_data is missing. Reload helpers.R and rerun this chunk.")
}

# Stack H0 first and H1 second, identifying the model for every loading.
parameter_estimates <- dplyr::bind_rows(
  H0 = parameter_display_h0,
  H1 = parameter_display_h1,
  .id = "Modelo"
)

# Export exactly the content displayed in the combined table.
readr::write_excel_csv2(
  parameter_estimates,
  here::here("Output", "Results", "Tables", "tbl-estimation-parameters.csv"),
  na = "NA"
)

# Present the two model blocks in one table.
knitr::kable(parameter_estimates, align = c("l", "l", "r", "r", "r", "r"),
  row.names = FALSE)

6.3 Convergência e admissibilidade

A verificação de convergência e admissibilidade encerra formalmente a etapa de estimação. A convergência atesta que o algoritmo encontrou uma solução estável para o modelo especificado. A admissibilidade matemática, por sua vez, é avaliada pela ausência de variâncias residuais negativas (casos Heywood) e de cargas padronizadas superiores à unidade (Brown, 2015; Nye, 2022). A Tabela 6.2 sintetiza essas verificações para H0 e H1, registrando o tamanho amostral efetivo e o número de parâmetros livres.

Na subamostra de calibração, ambos os modelos convergiram satisfatoriamente, sem apresentar estimativas impróprias de variância nem cargas padronizadas superiores a 1. As soluções mostram-se matematicamente admissíveis e aptas para as avaliações de ajuste global e local.

Tabela 6.2: Verificações de convergência e admissibilidade das soluções estimadas.
Modelo N observado Parâmetros livres Convergência Variâncias negativas Cargas padronizadas > 1
H0 621 79 Sim 0 0
H1 621 82 Sim 0 0

Finalidade

Atestar a convergência e a admissibilidade paramétrica dos dois modelos estimados, sumarizando o tamanho amostral e o número de parâmetros livres.

Preparação

Utilizam-se fit_analyzemodel e fit_h1modelfree, estimados no bloco da Tabela 6.1.

Procedimento

A lista diagnostic_fits viabiliza a inspeção padronizada de ambos os modelos. Para cada ajuste, o código recupera o status de convergência e o número de observações, calcula os parâmetros livres e examina variâncias e cargas padronizadas. As contagens registram variâncias negativas e cargas padronizadas maiores que 1.

O objeto estimation_checks reúne uma linha por modelo. A tabela apresentada e o CSV derivam do mesmo objeto, mantendo uma cópia dos diagnósticos para auditoria.

Execução e resultado

Execute o bloco para apresentar e salvar tbl-estimation-checks.csv em Output/Results/Tables, utilizando os ajustes existentes.

Código
# Collect the fitted models for diagnostic checks.
diagnostic_fits <- list(
  H0 = fit_analyzemodel,
  H1 = fit_h1modelfree
)

# Check convergence and admissibility for each fitted model.
estimation_checks <- purrr::imap_dfr(
  diagnostic_fits,
  function(fit, model_name) {
    # Identify the freely estimated parameters.
    parameter_table <- lavaan::parTable(fit)
    free_parameters <- parameter_table |>
      dplyr::filter(free > 0)

    # Extract the estimated variances.
    variance_table <- lavaan::parameterEstimates(fit) |>
      dplyr::filter(op == "~~", lhs == rhs)

    # Extract the standardized factor loadings.
    standardized_loadings <- lavaan::standardizedSolution(fit) |>
      dplyr::filter(op == "=~")

    # Count improper estimates and check convergence.
    negative_variances <- sum(variance_table$est < 0, na.rm = TRUE)
    heywood_loadings <- sum(
      abs(standardized_loadings$est.std) > 1,
      na.rm = TRUE
    )
    converged <- isTRUE(lavaan::lavInspect(fit, "converged"))

    # Summarize the diagnostic checks for the current model.
    tibble::tibble(
      Modelo = model_name,
      `N observado` = as.integer(lavaan::lavInspect(fit, "nobs")),
      `Parâmetros livres` = sum(free_parameters$free > 0),
      Convergência = if (converged) "Sim" else "Não",
      `Variâncias negativas` = negative_variances,
      `Cargas padronizadas > 1` = heywood_loadings
    )
  }
)

# Save the convergence and admissibility checks.
write.csv2(
  estimation_checks,
  here::here("Output", "Results", "Tables", "tbl-estimation-checks.csv"),
  row.names = FALSE,
  na = "NA"
)

# Present the convergence and admissibility checks.
estimation_checks |>
  knitr::kable()