6 Processo de estimação
O capítulo utiliza os scripts locais models.R e helpers.R, além da base whoqol_analysis.csv. Os modelos empíricos são os objetos analyzemodel e h1modelfree, definidos em models.R. O primeiro corresponde ao H0, com quatro fatores correlacionados e a covariância residual entre Q3 e Q4. O segundo corresponde ao H1 e acrescenta os três cross-loadings previamente justificados: Q8 e Q9 em psycho, e Q15 em environment.
Depois da seleção dos modelos e do pré-processamento, esta seção estima as duas especificações da AFC na amostra de análise final. A estimação converte as relações previstas nos modelos em parâmetros amostrais e aplica as condições definidas nas etapas anteriores: tratamento contínuo dos itens, MLR, FIML e identificação pela variância dos fatores (Jackson et al., 2009; Nye, 2022).
6.1 Fundamentação da estimação
Os itens foram tratados como variáveis contínuas, conforme a justificativa desenvolvida na Seção 2.4. Segundo Rhemtulla et al. (2012), a MLR apresenta desempenho adequado em condições com cinco ou mais categorias, especialmente com limiares aproximadamente simétricos. Robitzsch (2020) e Robitzsch (2022) mostram que as abordagens contínua e ordinal definem alvos paramétricos distintos. Com cinco categorias e uma estrutura fatorial previamente definida, este estudo adota a formulação contínua como aproximação de mensuração.
O método MLR foi mantido porque combina a máxima verossimilhança com erros-padrão robustos e estatística de teste escalada. Essa combinação é adequada às distribuições observadas na etapa de pré-processamento e segue a recomendação da literatura para situações de não normalidade multivariada (Brown, 2015; Kline, 2023; Rogers, 2024).
As poucas respostas ausentes foram incorporadas por FIML. O procedimento utiliza a informação observada de cada participante e mantém os 1.243 casos da amostra analítica. A escolha segue a recomendação de tratar ausências na etapa de estimação quando se trabalha com ML ou suas variantes robustas (Brown, 2023; Kline, 2023).
A escala dos fatores foi definida pela fixação de suas variâncias em 1. Essa identificação permite estimar livremente as cargas fatoriais e estabelece a mesma métrica latente nos dois modelos. As duas especificações foram estimadas na mesma amostra e sob as mesmas condições. A comparação concentra-se, assim, nas três relações adicionais do modelo H1(Jackson et al., 2009; Nye, 2022).
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 reúne a carga não padronizada, o erro-padrão robusto, a estatística de teste e a carga padronizada, conforme recomendado por Jackson et al. (2009) e Nye (2022).
As cargas não padronizadas preservam a métrica do modelo. As cargas padronizadas permitem comparar a magnitude das relações dentro de cada especificação, e os erros-padrão refletem a inferência robusta do MLR.
A tabela inclui os dois modelos e mostra as três relações adicionais do H1 junto das cargas primárias. A avaliação dessas relações combina os parâmetros estimados com os índices de ajuste e os critérios de comparação dos modelos.
| Modelo | Fator | Item | Carga | Erro-padrão | z | Carga padronizada |
|---|---|---|---|---|---|---|
| H0 | psycho | Q5 | 0.542 | 0.023 | 23.989 | 0.663 |
| H0 | psycho | Q6 | 0.620 | 0.028 | 22.061 | 0.636 |
| H0 | psycho | Q7 | 0.556 | 0.023 | 24.291 | 0.648 |
| H0 | psycho | Q11 | 0.609 | 0.028 | 21.676 | 0.612 |
| H0 | psycho | Q19 | 0.772 | 0.022 | 35.762 | 0.820 |
| H0 | psycho | Q26 | 0.541 | 0.028 | 19.157 | 0.581 |
| H0 | physical | Q3 | 0.395 | 0.030 | 13.150 | 0.420 |
| H0 | physical | Q4 | 0.387 | 0.032 | 12.025 | 0.384 |
| H0 | physical | Q10 | 0.650 | 0.023 | 28.113 | 0.720 |
| H0 | physical | Q15 | 0.380 | 0.028 | 13.513 | 0.471 |
| H0 | physical | Q16 | 0.559 | 0.028 | 19.973 | 0.541 |
| H0 | physical | Q17 | 0.819 | 0.020 | 40.897 | 0.890 |
| H0 | physical | Q18 | 0.802 | 0.023 | 35.540 | 0.840 |
| H0 | social | Q20 | 0.708 | 0.023 | 30.520 | 0.795 |
| H0 | social | Q21 | 0.513 | 0.030 | 17.008 | 0.509 |
| H0 | social | Q22 | 0.637 | 0.025 | 25.597 | 0.721 |
| H0 | environment | Q8 | 0.466 | 0.029 | 15.915 | 0.477 |
| H0 | environment | Q9 | 0.504 | 0.027 | 18.719 | 0.570 |
| H0 | environment | Q12 | 0.758 | 0.029 | 26.024 | 0.690 |
| H0 | environment | Q13 | 0.507 | 0.027 | 18.649 | 0.613 |
| H0 | environment | Q14 | 0.648 | 0.027 | 23.623 | 0.678 |
| H0 | environment | Q23 | 0.485 | 0.029 | 16.894 | 0.511 |
| H0 | environment | Q24 | 0.465 | 0.031 | 15.090 | 0.483 |
| H0 | environment | Q25 | 0.492 | 0.031 | 16.075 | 0.503 |
| H1 | psycho | Q5 | 0.541 | 0.023 | 23.884 | 0.662 |
| H1 | psycho | Q6 | 0.621 | 0.028 | 22.037 | 0.638 |
| H1 | psycho | Q7 | 0.558 | 0.023 | 24.251 | 0.650 |
| H1 | psycho | Q11 | 0.609 | 0.028 | 21.691 | 0.612 |
| H1 | psycho | Q19 | 0.773 | 0.022 | 35.835 | 0.822 |
| H1 | psycho | Q26 | 0.541 | 0.028 | 19.167 | 0.582 |
| H1 | psycho | Q8 | 0.160 | 0.049 | 3.263 | 0.163 |
| H1 | psycho | Q9 | 0.108 | 0.043 | 2.512 | 0.122 |
| H1 | physical | Q3 | 0.388 | 0.030 | 12.960 | 0.413 |
| H1 | physical | Q4 | 0.382 | 0.032 | 11.849 | 0.378 |
| H1 | physical | Q10 | 0.644 | 0.023 | 27.504 | 0.713 |
| H1 | physical | Q15 | 0.221 | 0.032 | 6.839 | 0.274 |
| H1 | physical | Q16 | 0.558 | 0.028 | 19.818 | 0.540 |
| H1 | physical | Q17 | 0.826 | 0.020 | 42.158 | 0.897 |
| H1 | physical | Q18 | 0.807 | 0.022 | 36.559 | 0.845 |
| H1 | social | Q20 | 0.709 | 0.023 | 30.508 | 0.796 |
| H1 | social | Q21 | 0.513 | 0.030 | 17.005 | 0.509 |
| H1 | social | Q22 | 0.637 | 0.025 | 25.560 | 0.721 |
| H1 | environment | Q8 | 0.329 | 0.050 | 6.559 | 0.336 |
| H1 | environment | Q9 | 0.410 | 0.045 | 9.187 | 0.464 |
| H1 | environment | Q12 | 0.769 | 0.029 | 26.536 | 0.700 |
| H1 | environment | Q13 | 0.516 | 0.027 | 18.977 | 0.624 |
| H1 | environment | Q14 | 0.656 | 0.028 | 23.846 | 0.687 |
| H1 | environment | Q23 | 0.482 | 0.029 | 16.694 | 0.509 |
| H1 | environment | Q24 | 0.470 | 0.031 | 15.098 | 0.488 |
| H1 | environment | Q25 | 0.497 | 0.031 | 15.953 | 0.508 |
| H1 | environment | Q15 | 0.257 | 0.033 | 7.851 | 0.319 |
O primeiro bloco lê a base analítica produzida no capítulo de pré-processamento. Em seguida, lavaan::cfa() ajusta os objetos analyzemodel e h1modelfree com máxima verossimilhança robusta (MLR), informação completa (FIML) para as ausências e identificação por variâncias latentes fixadas. Como os dois ajustes usam a mesma base e os mesmos argumentos, a diferença entre eles permanece restrita à especificação das cargas cruzadas.
lavaan::parameterEstimates(..., standardized = TRUE) reúne as estimativas não padronizadas, os erros-padrão robustos, as estatísticas de teste e as soluções padronizadas. O filtro op == "=~" mantém somente as relações de carga fatorial; variâncias, covariâncias fatoriais e variâncias residuais serão examinadas nas etapas apropriadas. A coluna std.all fornece a carga padronizada, enquanto est, se e z preservam, respectivamente, a carga na escala do ajuste, sua incerteza e a estatística de teste.
As tabelas específicas de H0 e H1 são combinadas por dplyr::bind_rows() e recebem a coluna Modelo, o que permite apresentar os resultados lado a lado sem repetir a mesma tabela no texto. write.csv2() salva a versão completa, com a precisão original, em Output/Results/Tables; essa exportação é útil para auditoria e reutilização independente da renderização. Na saída exibida, dplyr::mutate() arredonda apenas uma cópia destinada à apresentação, sem alterar o arquivo salvo. Por fim, knitr::kable() transforma essa cópia em uma tabela formatada para o HTML e o PDF.
Código
analysis_data <- readr::read_csv2(
here::here("Data", "AnalysisData", "whoqol_analysis.csv"),
na = c("", "NA"),
show_col_types = FALSE
)
fit_analyzemodel <- lavaan::cfa(
model = analyzemodel,
data = analysis_data,
estimator = "MLR",
missing = "fiml",
std.lv = TRUE
)
fit_h1modelfree <- lavaan::cfa(
model = h1modelfree,
data = analysis_data,
estimator = "MLR",
missing = "fiml",
std.lv = TRUE
)
parameter_estimates_analyzemodel <- lavaan::parameterEstimates(
fit_analyzemodel,
standardized = TRUE
) |>
dplyr::filter(op == "=~") |>
dplyr::transmute(
Modelo = "H~0~",
Fator = lhs,
Item = rhs,
Carga = est,
`Erro-padrão` = se,
z = z,
`Carga padronizada` = std.all
)
parameter_estimates_h1modelfree <- lavaan::parameterEstimates(
fit_h1modelfree,
standardized = TRUE
) |>
dplyr::filter(op == "=~") |>
dplyr::transmute(
Modelo = "H~1~",
Fator = lhs,
Item = rhs,
Carga = est,
`Erro-padrão` = se,
z = z,
`Carga padronizada` = std.all
)
parameter_estimates <- dplyr::bind_rows(
parameter_estimates_analyzemodel,
parameter_estimates_h1modelfree
)
write.csv2(
parameter_estimates,
here::here("Output", "Results", "Tables", "tbl-estimation-parameters.csv"),
row.names = FALSE,
na = "NA"
)
parameter_estimates |>
dplyr::mutate(
dplyr::across(
c(Carga, `Erro-padrão`, z, `Carga padronizada`),
~ round(.x, 3)
)
) |>
knitr::kable(escape = FALSE)6.3 Convergência e admissibilidade
Convergência e admissibilidade encerram a etapa de estimação. A convergência indica que o algoritmo encontrou uma solução para o modelo especificado. A admissibilidade é examinada pelas variâncias negativas e pelas cargas padronizadas superiores a 1, diagnósticos associados a soluções Heywood (Brown, 2015; Nye, 2022). A Tabela 6.2 reúne essas verificações para H0 e H1 e registra o tamanho amostral e o número de parâmetros livres.
Os dois modelos convergiram, não apresentaram variâncias negativas e não produziram cargas padronizadas superiores a 1. As soluções estão prontas para a avaliação do ajuste global e local.
| Modelo | N observado | Parâmetros livres | Convergência | Variâncias negativas | Cargas padronizadas > 1 |
|---|---|---|---|---|---|
| H0 | 1243 | 79 | TRUE | 0 | 0 |
| H1 | 1243 | 82 | TRUE | 0 | 0 |
lavInspect(fit, "converged") identifica se o algoritmo terminou com uma solução convergida. A tabela de parâmetros é inspecionada para localizar variâncias negativas e cargas padronizadas maiores que 1, que podem caracterizar soluções Heywood. O número de parâmetros livres é recuperado para documentar a complexidade estimada de cada especificação.
O objeto diagnostic_fits apenas reúne os dois ajustes já estimados para que a mesma sequência de inspeções seja aplicada a H0 e H1; ele não realiza uma nova estimação. lavaan::parTable() fornece a coluna free, que permite contar os parâmetros efetivamente estimados, enquanto lavaan::lavInspect(..., "nobs") recupera o tamanho amostral utilizado pelo ajuste e lavaan::lavInspect(..., "converged") informa se o algoritmo convergiu.
As variâncias são obtidas com lavaan::parameterEstimates() filtrando as relações de variância (op == "~~" e lhs == rhs). As cargas padronizadas são recuperadas com lavaan::standardizedSolution() e filtradas para as relações de carga (op == "=~"). A contagem de valores negativos e de cargas maiores que 1 resume os dois diagnósticos de admissibilidade apresentados na tabela.
Depois dessas operações, tibble::tibble() organiza uma linha para cada modelo e somente as seis colunas relevantes para esta etapa. write.csv2() salva essa tabela diagnóstica em Output/Results/Tables/tbl-estimation-checks.csv, preservando uma cópia tabular para auditoria. A chamada final a knitr::kable() apresenta o mesmo objeto no HTML e no PDF; portanto, a tabela exibida e o CSV derivam da mesma estrutura, sem uma reconstrução posterior do resultado.
Código
diagnostic_fits <- list(
H0 = fit_analyzemodel,
H1 = fit_h1modelfree
)
estimation_checks <- purrr::imap_dfr(
diagnostic_fits,
function(fit, model_name) {
parameter_table <- lavaan::parTable(fit)
free_parameters <- parameter_table |>
dplyr::filter(free > 0)
variance_table <- lavaan::parameterEstimates(fit) |>
dplyr::filter(op == "~~", lhs == rhs)
standardized_loadings <- lavaan::standardizedSolution(fit) |>
dplyr::filter(op == "=~")
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"))
tibble::tibble(
Modelo = model_name,
`N observado` = as.integer(lavaan::lavInspect(fit, "nobs")),
`Parâmetros livres` = sum(free_parameters$free > 0),
Convergência = converged,
`Variâncias negativas` = negative_variances,
`Cargas padronizadas > 1` = heywood_loadings
)
}
)
write.csv2(
estimation_checks,
here::here("Output", "Results", "Tables", "tbl-estimation-checks.csv"),
row.names = FALSE,
na = "NA"
)
estimation_checks |>
knitr::kable()