14  Rotação fatorial em R

A Análise Fatorial (AF) é uma técnica estatística poderosa usada para identificar estruturas latentes (fatores) subjacentes a um conjunto de variáveis observadas. No entanto, a solução matemática inicial de uma AF raramente é interpretável. É aqui que a rotação fatorial se torna a etapa mais crítica do processo. A rotação transforma a matriz de cargas fatoriais inicial em uma solução mais simples e teoricamente mais significativa, sem alterar as propriedades matemáticas fundamentais da solução.

Este documento oferece um exemplo prático e didático, totalmente focado em demonstrar o impacto das diferentes estratégias de rotação. Usaremos o software R e o clássico conjunto de dados bfi (Big Five Inventory) do pacote psych.

Objetivos:

  1. Comparar métodos de extração: Componentes Principais (PAF) e Máxima Verossimilhança (ML).
  2. Demonstrar a dificuldade de interpretar uma solução fatorial não rotacionada.
  3. Aplicar e interpretar uma rotação ortogonal (Varimax), que assume fatores não correlacionados.
  4. Aplicar e interpretar uma rotação oblíqua (Promax), que permite a correlação entre os fatores.
  5. Discutir como a escolha da rotação afeta a interpretação final e a validade teórica dos resultados.

14.1 Passo 1: Análise Descritiva e Adequação dos Dados

Primeiro, carregamos os pacotes necessários e o conjunto de dados bfi. Este dataset contém respostas de 2800 indivíduos a 25 itens de personalidade.

Código
# Carregar pacotes
library(psych)
library(ggplot2)
library(dplyr)
library(tidyr)
library(corrplot)

# Carregar os dados do Big Five Inventory
data(bfi, package = "psych")

# Selecionar apenas as 25 variáveis de itens de personalidade
bfi_items <- bfi[, 1:25]

# Remover linhas com dados ausentes para simplificar
bfi_complete <- na.omit(bfi_items)

knitr::kable(head(bfi_complete))
Tabela 14.1: Exemplo de respostas no banco de dados Big Five
A1 A2 A3 A4 A5 C1 C2 C3 C4 C5 E1 E2 E3 E4 E5 N1 N2 N3 N4 N5 O1 O2 O3 O4 O5
61617 2 4 3 4 4 2 3 3 4 4 3 3 3 4 4 3 4 2 2 3 3 6 3 4 3
61618 2 4 5 2 5 5 4 4 3 4 1 1 6 4 3 3 3 3 5 5 4 2 4 3 3
61620 5 4 5 4 4 4 5 4 2 5 2 4 4 4 5 4 5 4 2 3 4 2 5 5 2
61621 4 4 6 5 5 4 4 3 5 5 5 3 4 4 4 2 5 2 4 1 3 3 4 3 5
61622 2 3 3 4 5 4 4 5 3 2 2 2 5 4 5 2 3 4 4 3 3 3 4 3 3
61623 6 6 5 6 5 6 6 6 1 3 2 1 6 5 6 3 5 2 2 3 4 3 5 6 1

As 25 variáveis correspondem a 5 itens para cada um dos traços do “Big Five”:

  • A1-A5: Amabilidade (Agreeableness)
  • C1-C5: Conscienciosidade (Conscientiousness)
  • E1-E5: Extroversão (Extraversion)
  • N1-N5: Neuroticismo (Neuroticism)
  • O1-O5: Abertura à Experiência (Openness)

A hipótese teórica é que os 5 itens que medem o mesmo traço (e.g., N1 a N5) estarão altamente correlacionados entre si e se agruparão em um único fator latente (Neuroticismo).

Uma boa prática é verificar se os dados são fatorizáveis. Para isso, podemos usar o teste de Bartlett e a medida KMO.

Código
# Teste de Bartlett
bartlett_test <- cortest.bartlett(bfi_complete)
R was not square, finding R from data
Código
# Teste KMO
kmo_test <- KMO(bfi_complete)

# Exibindo os resultados de forma concisa
cat("Teste de Bartlett: p-valor =", bartlett_test$p.value, "\n")
Teste de Bartlett: p-valor = 0 
Código
cat("Medida KMO Geral (Overall MSA):", round(kmo_test$MSA, 2), "\n")
Medida KMO Geral (Overall MSA): 0.85 

O p-valor de Bartlett próximo de zero e o KMO de 0.85 (“meritório”) sugerem que os dados têm correlações suficientes para justificar uma análise fatorial.

14.2 Passo 2: Extração Inicial dos Fatores e Escolha do Número de Fatores

Antes de rotacionar, precisamos extrair os fatores. Dois métodos comuns são a Fatoração do Eixo Principal (ou “componentes principais” para o modelo fatorial) e a Máxima Verossimilhança. Vamos extrair 5 fatores usando ambos os métodos (sem rotação) para ver a solução inicial.

Código
# Extração via Fatoração do Eixo Principal (Principal Axis Factoring)
fa_pa <- fa(bfi_complete, nfactors = 5, rotate = "none", fm = "pa")
cat("Cargas - Fatoração do Eixo Principal (PA):\n")
Cargas - Fatoração do Eixo Principal (PA):
Código
print(fa_pa$loadings, cutoff = 0.3)

Loadings:
   PA1    PA2    PA3    PA4    PA5   
A1                             -0.371
A2  0.467                       0.340
A3  0.534  0.302                     
A4  0.417                            
A5  0.581                            
C1  0.343         0.446              
C2  0.336         0.477              
C3  0.319         0.351  0.310       
C4 -0.465        -0.452              
C5 -0.493                            
E1 -0.408                            
E2 -0.619                       0.323
E3  0.527  0.328                     
E4  0.599        -0.329              
E5  0.513                            
N1 -0.441  0.636                     
N2 -0.423  0.616                     
N3 -0.407  0.611                     
N4 -0.528  0.416                     
N5 -0.345  0.413                     
O1  0.328               -0.360       
O2                       0.370       
O3  0.407               -0.446       
O4                                   
O5                       0.412       

                 PA1   PA2   PA3   PA4   PA5
SS loadings    4.600 2.268 1.549 1.218 0.956
Proportion Var 0.184 0.091 0.062 0.049 0.038
Cumulative Var 0.184 0.275 0.337 0.385 0.424
Código
# Extração via Máxima Verossimilhança (Maximum Likelihood)
fa_ml <- fa(bfi_complete, nfactors = 5, rotate = "none", fm = "ml")
cat("\nCargas - Máxima Verossimilhança (ML):\n")

Cargas - Máxima Verossimilhança (ML):
Código
print(fa_ml$loadings, cutoff = 0.3)

Loadings:
   ML1    ML2    ML3    ML4    ML5   
A1                             -0.322
A2 -0.396  0.354                0.334
A3 -0.462  0.401                0.321
A4 -0.386                            
A5 -0.546                            
C1                0.465              
C2                0.511              
C3                0.404              
C4  0.441        -0.512              
C5  0.485        -0.358              
E1  0.355 -0.309                     
E2  0.585                       0.336
E3 -0.446  0.436                     
E4 -0.552  0.333                     
E5 -0.409  0.429                     
N1  0.609  0.566                     
N2  0.587  0.543                     
N3  0.533  0.479                     
N4  0.591                            
N5  0.421                            
O1                      -0.409       
O2                       0.388       
O3 -0.329  0.349        -0.491       
O4                      -0.307  0.311
O5                       0.433       

                 ML1   ML2   ML3   ML4   ML5
SS loadings    4.451 2.379 1.546 1.221 0.977
Proportion Var 0.178 0.095 0.062 0.049 0.039
Cumulative Var 0.178 0.273 0.335 0.384 0.423

As duas soluções iniciais são numericamente diferentes, mas conceitualmente iguais: são ininterpretáveis. Um primeiro fator geral domina, e as variáveis se distribuem de forma confusa nos demais. Isso reforça a necessidade da rotação.

Para determinar o número de fatores a extrair de forma mais objetiva, usamos a Análise Paralela.

Código
fa.parallel(bfi_complete, fa = "fa", fm = "pa")
Parallel analysis suggests that the number of factors =  6  and the number of components =  NA 
Figura 14.1: Análise Paralela sugerindo a extração de 6 fatores.

A Análise Paralela (Figura 14.1) sugere 6 fatores. No entanto, como nosso objetivo é testar a teoria dos Big Five, prosseguiremos com a extração de 5 fatores, uma decisão comum quando a teoria é forte.

14.3 Passo 3: Rotação Ortogonal (Varimax)

A rotação Varimax “limpa” a estrutura sob a suposição de que os fatores não são correlacionados entre si.

Código
# Análise Fatorial com rotação Varimax
fa_varimax <- factanal(bfi_complete, 
                       factors = 5, 
                       rotation = "varimax")

cat("Cargas Fatoriais (AF) - Rotação Varimax:\n")
Cargas Fatoriais (AF) - Rotação Varimax:
Código
print(fa_varimax$loadings, cutoff = 0.3, sort = TRUE)

Loadings:
   Factor1 Factor2 Factor3 Factor4 Factor5
N1  0.816                                 
N2  0.787                                 
N3  0.714                                 
N4  0.562  -0.367                         
N5  0.518                                 
E1         -0.587                         
E2         -0.674                         
E4          0.613           0.363         
C1                  0.533                 
C2                  0.624                 
C3                  0.554                 
C4                 -0.653                 
C5                 -0.573                 
A2                          0.601         
A3                          0.662         
A5          0.351           0.580         
O1                                  0.524 
O3                                  0.614 
O5                                 -0.512 
A1                         -0.393         
A4                          0.454         
E3          0.490           0.315   0.313 
E5          0.491   0.310                 
O2                                 -0.454 
O4                                  0.368 

               Factor1 Factor2 Factor3 Factor4 Factor5
SS loadings      2.687   2.320   2.034   1.978   1.557
Proportion Var   0.107   0.093   0.081   0.079   0.062
Cumulative Var   0.107   0.200   0.282   0.361   0.423

A estrutura agora é muito mais “simples” e alinhada com a teoria. Podemos visualizar essa transformação de forma clara comparando o círculo de correlações antes e depois da rotação.

Primeiro, a solução não rotacionada (Figura 14.2). Note como as variáveis (vetores) se espalham pelo espaço fatorial sem um padrão claro. É difícil traçar os eixos (fatores) de forma que representem grupos distintos de variáveis.

Código
library(ggrepel)

# Extrair cargas da solução NÃO ROTACIONADA (ml) para um dataframe
loadings_unrotated_df <- as.data.frame(unclass(fa_ml$loadings))
loadings_unrotated_df$Variable <- rownames(loadings_unrotated_df)

# Selecionar algumas variáveis para anotar e evitar poluição
vars_to_label <- c("N1", "N3", "E2", "E4", "A1", "C1", "O1")
annotations_df_unrotated <- loadings_unrotated_df %>% filter(Variable %in% vars_to_label)

# Criar dados para o círculo unitário
circle <- data.frame(
  angle = seq(-pi, pi, length = 100),
  x = sin(seq(-pi, pi, length = 100)),
  y = cos(seq(-pi, pi, length = 100))
)

# Gerar o gráfico com ggplot2
ggplot(data = loadings_unrotated_df, aes(x = ML1, y = ML2)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray70") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "gray70") +
  geom_path(data = circle, aes(x = x, y = y), inherit.aes = FALSE, color = "gray60") +
  geom_segment(aes(x = 0, y = 0, xend = ML1, yend = ML2),
               arrow = arrow(length = unit(0.1, "inches")),
               color = "steelblue") +
  geom_text_repel(data = annotations_df_unrotated, aes(label = Variable), min.segment.length = 0) +
  coord_fixed(ratio = 1, xlim = c(-1.1, 1.1), ylim = c(-1.1, 1.1)) +
  labs(title = "Círculo de Correlações - Solução Não Rotacionada",
       x = "Fator 1",
       y = "Fator 2") +
  theme_minimal()
Figura 14.2: Círculo de correlações da solução não rotacionada. Os vetores não estão alinhados com os eixos.

Agora, veja o resultado após a rotação Varimax (Figura 14.3). A rotação funcionou como um ajuste dos eixos, alinhando-os com os agrupamentos de variáveis.

Código
# Extrair cargas da solução ROTACIONADA (Varimax) para um dataframe
loadings_rotated_df <- as.data.frame(unclass(fa_varimax$loadings))
loadings_rotated_df$Variable <- rownames(loadings_rotated_df)

# Selecionar as mesmas variáveis para anotar
annotations_df_rotated <- loadings_rotated_df %>% filter(Variable %in% vars_to_label)

# Calcular a variância explicada para os eixos
ss_loadings <- colSums(fa_varimax$loadings^2)
prop_variance <- ss_loadings / ncol(bfi_complete)
xlab_text <- sprintf("Fator 1 (%.2f%% da variância)", prop_variance[1] * 100)
ylab_text <- sprintf("Fator 2 (%.2f%% da variância)", prop_variance[2] * 100)

# Gerar o gráfico com ggplot2
ggplot(data = loadings_rotated_df, aes(x = Factor1, y = Factor2)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray70") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "gray70") +
  geom_path(data = circle, aes(x = x, y = y), inherit.aes = FALSE, color = "gray60") +
  geom_segment(aes(x = 0, y = 0, xend = Factor1, yend = Factor2),
               arrow = arrow(length = unit(0.1, "inches")),
               color = "steelblue") +
  geom_text_repel(data = annotations_df_rotated, aes(label = Variable), min.segment.length = 0) +
  coord_fixed(ratio = 1, xlim = c(-1.1, 1.1), ylim = c(-1.1, 1.1)) +
  labs(title = "Círculo de Correlações - Rotação Varimax",
       x = xlab_text,
       y = ylab_text) +
  theme_minimal()
Figura 14.3: Círculo de correlações após a rotação Varimax. A rotação alinhou os vetores com os eixos, revelando uma estrutura simples.

O resultado é uma “estrutura simples”, onde os itens de Neuroticismo (como N1 e N3) carregam quase exclusivamente no Fator 1 (correlação próxima de 1 ou -1 em um eixo e de 0 no outro), e os itens de Extroversão (como E2 e E4) carregam no Fator 2. A interpretação se torna direta.

14.4 Passo 4: Rotação Oblíqua (Promax)

A rotação Promax é mais flexível, pois permite que os fatores sejam correlacionados. Isso costuma ser mais realista em psicologia.

Código
# Análise Fatorial com rotação Promax (oblíqua)
fa_promax <- fa(bfi_complete, 
                nfactors = 5, 
                rotate = "promax", 
                fm = "pa")
Loading required namespace: GPArotation
Código
cat("Cargas Fatoriais (Pattern Matrix) - Rotação Promax:\n")
Cargas Fatoriais (Pattern Matrix) - Rotação Promax:
Código
print(fa_promax$loadings, cutoff = 0.3, sort = TRUE)

Loadings:
   PA2    PA1    PA3    PA5    PA4   
N1  0.835                            
N2  0.791                            
N3  0.741                            
N4  0.533 -0.311                     
N5  0.529                            
E1        -0.636                     
E2        -0.711                     
E3         0.545                     
E4         0.660                     
C1                0.567              
C2                0.697              
C3                0.597              
C4               -0.652              
C5               -0.561              
A2                       0.611       
A3                       0.620       
O3                              0.576
O5                             -0.543
A1                      -0.463       
A4                       0.411       
A5         0.332         0.489       
E5         0.498                     
O1                              0.491
O2                             -0.484
O4                              0.370

                 PA2   PA1   PA3   PA5   PA4
SS loadings    2.704 2.486 2.050 1.638 1.461
Proportion Var 0.108 0.099 0.082 0.066 0.058
Cumulative Var 0.108 0.208 0.290 0.355 0.414

A matriz de cargas é similar à da Varimax, mas a grande vantagem é poder examinar a matriz de correlação entre os fatores.

Código
# Matriz de correlação entre os fatores
factor_correlations <- fa_promax$Phi

cat("Matriz de Correlação entre os Fatores (Promax):\n")
Matriz de Correlação entre os Fatores (Promax):
Código
print(round(factor_correlations, 2))
      PA2   PA1   PA3   PA5  PA4
PA2  1.00 -0.26 -0.22 -0.01 0.04
PA1 -0.26  1.00  0.40  0.35 0.14
PA3 -0.22  0.40  1.00  0.24 0.19
PA5 -0.01  0.35  0.24  1.00 0.16
PA4  0.04  0.14  0.19  0.16 1.00
Código
# Visualização da matriz de correlação
corrplot(factor_correlations, method = "color", type = "upper", 
         addCoef.col = "black", tl.col = "black", tl.srt = 45, diag = FALSE)
Figura 14.4: Correlações entre os fatores da solução Promax.

A matriz de correlação (Figura 14.4) mostra que Neuroticismo (ML1) se correlaciona negativamente com Conscienciosidade (ML3) (-0.33) e Extroversão (ML2) (-0.24). Essas relações são teoricamente plausíveis e seriam perdidas em uma rotação ortogonal.

14.5 Conclusão: Qual Rotação Escolher?

  • Solução Não Rotacionada: É apenas um ponto de partida matemático. Dificilmente é possível tirar interpretações úteis dela.
  • Rotação Ortogonal (Varimax): É a melhor escolha quando há fortes razões teóricas para acreditar que os fatores são independentes. Oferece uma solução mais simples (parcimoniosa).
  • Rotação Oblíqua (Promax): É uma escolha mais realista nas ciências sociais. A decisão final deve ser baseada na matriz de correlação dos fatores. Se as correlações forem substanciais, a solução oblíqua é superior.

Neste exemplo, a solução Promax (oblíqua) é a mais apropriada. Ela não apenas recupera a estrutura dos Big Five, mas também fornece insights sobre como esses traços se relacionam, oferecendo uma visão mais rica e fiel da realidade psicológica.